Practical Programming in TCL and TK
Practical Programming in TCL and TK
Welch
Publisher: Prentice Hall PTR Pub Date: November 10, 1999 ISBN: 0-13-022028-0 Pages: 832 Supplier: Team FLY
Tcl/Tk 8.2 is the first scripting language that can handle enterprise-wide integration tasks that encompass Windows, Solaris, Macintosh, and other key platforms. Now, in this fully updated Third Edition, Tcl/Tk development team member and best-selling author Brent Welch presents all you need to know to achieve powerful results with Tcl/Tk 8.2 and the new Tcl Web Server. Coverage includes:
Tcl's fundamental mechanisms and operating system interfaces Basic and advanced coding techniques and tools, including the Tcl script library facility Tk and X Windows-with detailed examples and sample widgets The new, extensible Tcl Web Server New Tcl internationalization features and thread support New techniques for working with regular expressions and namespaces You'll find extensive coverage of user interface development, as well as application integration techniques that leverage Tcl/Tk's powerful cross-platform scripting capabilities. Welch covers Tcl's extensive network support, as well as Safe Tcl, C programming with the Tk toolkit, the Tcl compiler, and Tcl/Tk plug-ins for Netscape and Internet Explorer. Whether you're a current Tcl/Tk programmer, or a developer searching for a convenient, powerful multiplatform scripting language, Practical Programming in Tcl and Tk, Third Edition delivers exactly what you're looking for. "This is an excellent book, loaded with useful examples. Newcomers to Tk will find the widget descriptions particularly helpful." -John Ousterhout CEO and founder of Scriptics Corporation and the creator of Tcl/Tk "Brent Welch fills an important need for an introduction to Tcl/Tk with an applied focus and with coverage of many of the useful extensions available . . . I recommend this book to my new students . . . and I keep a copy handy for my own use." -Joseph A. Konstan, Professor of Computer Science University of Minnesota
Publisher: Prentice Hall PTR Pub Date: November 10, 1999 ISBN: 0-13-022028-0 Pages: 832 Supplier: Team FLY
Copyright List of Examples List of Tables Preface Why Tcl? Tcl and Tk Versions Who Should Read This Book How to Read This Book Other Tcl Books On-line Examples Ftp Archives World Wide Web Newsgroups Typographic Conventions Hot Tips Book Organization What's New in the Third Edition First Edition Thanks Second Edition Thanks Third Edition Thanks Contact the Author Part I. Tcl Basics Chapter 1. Tcl Fundamentals Tcl Commands Hello, World! Variables Command Substitution Math Expressions Backslash Substitution Grouping with Braces and Double Quotes Procedures A Factorial Example More about Variables More about Math Expressions
Comments Substitution and Grouping Summary Fine Points Reference Chapter 2. Getting Started The source Command UNIX Tcl Scripts Windows 95 Start Menu The Macintosh and ResEdit The console Command Command-Line Arguments Predefined Variables Chapter 3. The Guestbook CGI Application A Quick Introduction to HTML CGI for Dynamic Pages The guestbook.cgi Script Defining Forms and Processing Form Data The cgi.tcl Package Next Steps Chapter 4. String Processing in Tcl The string Command The append Command The format Command The scan Command The binary Command Related Chapters Chapter 5. Tcl Lists Tcl Lists Constructing Lists Getting List Elements: llength, lindex, and lrange Modifying Lists: linsert and lreplace Searching Lists: lsearch Sorting Lists: lsort The split Command The join Command Related Chapters Chapter 6. Control Structure Commands
If Then Else Switch While Foreach For Break Catch Error Return
and Continue
Chapter 7. Procedures and Scope The proc Command Changing Command Names with rename Scope The global Command Call by Name Using upvar Variable Aliases with upvar Chapter 8. Tcl Arrays Array Syntax The array Command Building Data Structures with Arrays Chapter 9. Working with Files and Programs Running Programs with exec The file Command Cross-Platform File Naming Manipulating Files and Directories File Attributes Input/Output Command Summary Opening Files for I/O Reading and Writing The Current Directory ?cd and pwd Matching File Names with glob The exit and pid Commands Environment Variables The registry Command Part II. Advanced Tcl Chapter 10. Quoting Issues and Eval Constructing Code with the list Command Exploiting the concat inside eval The uplevel Command The subst Command Chapter 11. Regular Expressions When to Use Regular Expressions Regular Expression Syntax Advanced Regular Expressions Syntax Summary The regexp Command The regsub Command Transforming Data to Program with regsub Other Commands That Use Regular Expressions Chapter 12. Script Libraries and Packages Locating Packages: The auto_path Variable Using Packages Summary of Package Loading The package Command Libraries Based on the tclIndex File
The unknown Command Interactive Conveniences Tcl Shell Library Environment Coding Style Chapter 13. Reflection and Debugging The clock Command The info Command Cross-Platform Support Tracing Variable Values Interactive Command History Debugging Scriptics' TclPro Other Tools Performance Tuning Chapter 14. Namespaces Using Namespaces Namespace Variables Command Lookup Nested Namespaces Importing and Exporting Procedures Callbacks and Namespaces Introspection The namespace Command Converting Existing Packages to use Namespaces
[incr Tcl]
Object System
Notes Chapter 15. Internationalization Character Sets and Encodings Message Catalogs Chapter 16. Event-Driven Programming The Tcl Event Loop The after Command The fileevent Command The vwait Command The fconfigure Command Chapter 17. Socket Programming Client Sockets Server Sockets The Echo Service Fetching a URL with HTTP The http Package Basic Authentication Chapter 18. TclHttpd Web Server Integrating TclHttpd with your Application Domain Handlers Application Direct URLs
Document Types HTML + Tcl Templates Form Handlers Programming Reference Standard Application-Direct URLs The TclHttpd Distribution Server Configuration Chapter 19. Multiple Interpreters and Safe-Tcl The interp Command Creating Interpreters Safe Interpreters Command Aliases Hidden Commands Substitutions I/O from Safe Interpreters The Safe Base Security Policies Chapter 20. Safe-Tk and the Browser Plugin Tk in Child Interpreters The Browser Plugin Security Policies and Browser Plugin Configuring Security Policies Part III. Tk Basics Chapter 21. Tk Fundamentals Hello, World! in Tk Naming Tk Widgets Configuring Tk Widgets Tk Widget Attributes and the Resource Database Summary of the Tk Commands Chapter 22. Tk by Example ExecLog The Example Browser A Tcl Shell Chapter 23. The Pack Geometry Manager Packing toward a Side Horizontal and Vertical Stacking The Cavity Model Packing Space and Display Space Resizing and -expand Anchoring Packing Order Choosing the Parent for Packing Unpacking a Widget Packer Summary Window Stacking Order Chapter 24. The Grid Geometry Manager
A Basic Grid Spanning Rows and Columns Row and Column Constraints The grid Command Chapter 25. The Place Geometry Managery
place
Basics
The Pane Manager The place Command Chapter 26. Binding Commands to Events The bind Command The bindtags Command Event Syntax Modifiers Event Sequences Virtual Events Event Keywords Part IV. Tk Widgets Chapter 27. Buttons and Menus Button Commands and Scope Issues Buttons Associated with Tcl Variables Button Attributes Button Operations Menus and Menubuttons Keyboard Traversal Manipulating Menus and Menu Entries Menu Attributes A Menu by Name Package Chapter 28. The Resource Database An Introduction to Resources Loading Option Database Files Adding Individual Database Entries Accessing the Database User-Defined Buttons User-Defined Menus Chapter 29. Simple Tk Widgets Frames and Toplevel Windows The Label Widget The Message Widget The Scale Widget The bell Command Chapter 30. Scrollbars Using Scrollbars The Scrollbar Protocol The Scrollbar Widget Chapter 31. The Entry Widget Using Entry Widgets
The Entry Widget Chapter 32. The Listbox Widget Using Listboxes Listbox Bindings Listbox Attributes Chapter 33. The Text Widget Text Indices Text Marks Text Tags The Selection Tag Bindings Searching Text Embedded Widgets Embedded Images Looking inside the Text Widget Text Bindings Text Operations Text Attributes Chapter 34. The Canvas Widget Canvas Coordinates Hello, World! The Min Max Scale Example Canvas Objects Canvas Operations Generating Postscript Canvas Attributes Hints Part V. Tk Details Chapter 35. Selections and the Clipboard The Selection Model The selection Command The clipboard Command Selection Handlers Chapter 36. Focus, Grabs, and Dialogs Standard Dialogs Custom Dialogs Animation with the update Command Chapter 37. Tk Widget Attributes Configuring Attributes Size Borders and Relief The Focus Highlight Padding and Anchors Chapter 38. Color, Images, and Cursors Colors Colormaps and Visuals
Bitmaps and Images The Text Insert Cursor The Mouse Cursor Chapter 39. Fonts and Text Attributes Naming a Font X Font Names Font Metrics The font Command Text Attributes Gridding, Resizing, and Geometry A Font Selection Application Chapter 40. Send The send Command The Sender Script Communicating Processes Remote eval through Sockets Chapter 41. Window Managers and Window Information The wm Command The winfo Command The tk Command Chapter 42. Managing User Preferences App-Defaults Files Defining Preferences The Preferences User Interface Managing the Preferences File Tracing Changes to Preference Variables Improving the Package Chapter 43. A User Interface to Bindings A Pair of Listboxes Working Together The Editing Interface Saving and Loading Bindings Part VI. C Programming Chapter 44. C Programming and Tcl Basic Concepts Creating a Loadable Package A C Command Procedure The blob Command Example Strings and Internationalization
Tcl_Main
and Tcl_AppInit
The Event Loop Invoking Scripts from C Chapter 45. Compiling Tcl and Extensions Standard Directory Structure Building Tcl from Source Using Stub Libraries Using autoconf
The Sample Extension Chapter 46. Writing a Tk Widget in C Initializing the Extension The Widget Data Structure The Widget Class Command The Widget Instance Command Configuring and Reconfiguring Attributes Specifying Widget Attributes Displaying the Clock The Window Event Procedure Final Cleanup Chapter 47. C Library Overview An Overview of the Tcl C Library An Overview of the Tk C Library Part VII. Changes Chapter 48. Tcl 7.4/Tk 4.0 wish Obsolete Features The cget Operation Input Focus Highlight Bindings Scrollbar Interface
pack info
Focus The send Command Internal Button Padding Radiobutton Value Entry Widget Menus Listboxes No geometry Attribute Text Widget Color Attributes Color Allocation and tk colormodel Canvas scrollincrement The Selection The bell Command Chapter 49. Tcl 7.5/Tk 4.1 Cross-Platform Scripts The clock Command The load Command The package Command Multiple foreach loop variables Event Loop Moves from Tk to Tcl Network Sockets Multiple Interpreters and Safe-Tcl
The grid Geometry Manager The Text Widget The Entry Widget Chapter 50. Tcl 7.6/Tk 4.2 More file Operations Virtual Events Standard Dialogs New grid Geometry Manager Macintosh unsupported1 Command Chapter 51. Tcl/Tk 8.0 The Tcl Compiler Namespaces Safe-Tcl New lsort
tcl_precision
Variable
Year 2000 Convention Http Package Serial Line I/O Platform-Independent Fonts The tk scaling Command Application Embedding Native Menus and Menubars CDE Border Width Native Buttons and Scrollbars Images in Text Widgets No Errors from destroy
grid rowconfigure
The Patch Releases Chapter 52. Tcl/Tk 8.1 Unicode and Internationalization Thread Safety Advanced Regular Expressions New String Commands The DDE Extension Miscellaneous Chapter 53. Tcl/Tk 8.2 The Trf Patch Faster String Operations Empty Array Names Brower Plugin Compatiblity Chapter 54. Tcl/Tk 8.3 Proposed Tcl Changes Proposed Tk Changes Chapter 55. About The CD-ROM Technical Support Index
Top
Table of Contents
Copyright
Library of Congress Cataloging-in-Publication Data Welch, Brent. B. Practical programming in Tcl and Tk / Brent B. Welch.-- 3rd ed. p. cm. ISBN 0-13-022028-0 1. Tcl (Computer program language) 2. Tk toolkit. I. Title. QA76.73.T44 W45 1999 005.13'3--dc21 99-047206
Credits
Editorial/Production Supervision: Joan L. McNamara Acquisitions Editor: Mark Taub Marketing Manager: Kate Hargett Editorial Assistant: Michael Fredette Cover Design Director: Jerry Votta Cover Design: Design Source Manufacturing Manager: Alexis R. Heydt 2000, 1997 by Prentice Hall PTR Prentice-Hall, Inc. Upper Saddle River, New Jersey 07458
Prentice Hall books are widely used by corporations and government agencies for training, marketing, and resale. The publisher offers discounts on this book when ordered in bulk quantities. For more information, contact: Corporate Sales Department, Prentice Hall PTR, One Lake Street, Upper Saddle River, NJ 07458 Phone: 800-382-3419; Fax: 201-236-7141; email: corpsales@prenhall.com All rights reserved. No part of this book may be reproduced, in any form or by any means, without permission in writing from the publisher. All product names mentioned herein are the trademarks of their respective owners. Printed in the United States of America 10 9 8 7 6 5 4 3 2 1 Prentice-Hall International (UK) Limited, London Prentice-Hall of Australia Pty. Limited, Sydney Prentice-Hall Canada Inc., Toronto Prentice-Hall Hispanoamericana, S.A., Mexico Prentice-Hall of India Private Limited, New Delhi Prentice-Hall of Japan, Inc., Tokyo Prentice-Hall (Singapore) Pte. Ltd., Singapore Editora Prentice-Hall do Brasil, Ltda., Rio de Janeiro
Dedication
to Jody, Christopher, Daniel, and Michael
Top
Table of Contents
List of Examples
1.1 The "Hello, World!" example 1.2 Tcl variables 1.3 Command substitution 1.4 Simple arithmetic 1.5 Nested commands 1.6 Built-in math functions 1.7 Grouping expressions with braces 1.8 Quoting special characters with backslash 1.9 Continuing long lines with backslashes 1.10 Grouping with double quotes vs. braces 1.11 Embedded command and variable substitution 1.12 Defining a procedure 1.13 A while loop to compute factorial 1.14 A recursive definition of factorial 1.15 Using set to return a variable value 1.16 Embedded variable references 1.17 Using info to determine if a variable exists 1.18 Controlling precision with tcl_precision 2.1 A standalone Tcl script on UNIX
2.2 A standalone Tk script on UNIX 2.3 Using /bin/sh to run a Tcl script 2.4 The EchoArgs script 3.1 A simple CGI script 3.2 Output of Example 3-1 3.3 The guestbook.cgi script 3.4 The Cgi_Header procedure 3.5 The Link command formats a hypertext link 3.6 Initial output of guestbook.cgi 3.7 Output of guestbook.cgi 3.8 The newguest.html form 3.9 The newguest.cgi script 4.1 Comparing strings with string compare 4.2 Comparing strings with string equal 4.3 Mapping Microsoft World special characters to ASCII 5.1 Constructing a list with the list command 5.2 Using lappend to add elements to a list 5.3 Using concat to splice lists together 5.4 Double quotes compared to the concat and list commands 5.5 Modifying lists with linsert and lreplace 5.6 Deleting a list element by value 5.7 Sorting a list using a comparison function 5.8 Use split to turn input data into Tcl lists 5.9 Implementing join in Tcl 6.1 A conditional if then else command 6.2 Chained conditional with elseif 6.3 Using switch for an exact match 6.4 Using switch with substitutions in the patterns
6.5 A switch with "fall through" cases 6.6 Comments in switch commands 6.7 A while loop to read standard input 6.8 Looping with foreach 6.9 Parsing command-line arguments 6.10 Using list with foreach 6.11 Multiple loop variables with foreach 6.12 Multiple value lists with foreach 6.13 A for loop 6.14 A standard catch phrase 6.15 A longer catch phrase 6.16 There are several possible return values from catch 6.17 Raising an error 6.18 Preserving errorInfo when calling error 6.19 Raising an error with return 7.1 Default parameter values 7.2 Variable number of arguments 7.3 Variable scope and Tcl procedures 7.4 A random number generator. 7.5 Print variable by name 7.6 Improved incr procedure 8.1 Using arrays 8.2 Referencing an array indirectly 8.3 Referencing an array indirectly using upvar 8.4 ArrayInvert inverts an array 8.5 Using arrays for records, version 1 8.6 Using arrays for records, version 2 8.7 Using a list to implement a stack
8.8 Using an array to implement a stack 8.9 A list of arrays 8.10 A list of arrays 8.11 A simple in-memory database 9.1 Using exec on a process pipeline 9.2 Comparing file modify times 9.3 Determining whether pathnames reference the same file 9.4 Opening a file for writing 9.5 A more careful use of open 9.6 Opening a process pipeline 9.7 Prompting for input 9.8 A read loop using gets 9.9 A read loop using read and split 9.10 Copy a file and translate to native format 9.11 Finding a file by name 9.12 Printing environment variable values 10.1 Using list to construct commands 10.2 Generating procedures dynamically with a template 10.3 Using eval with $args 10.4 lassign: list assignment with foreach 10.5 The File_Process procedure applies a command to each line of a file 11.1 Expanded regular expressions allow comments 11.2 Using regular expressions to parse a string 11.3 A pattern to match URLs 11.4 An advanced regular expression to match URLs 11.5 The Url_Decode procedure 11.6 The Cgi_Parse and Cgi_Value procedures 11.7 Cgi_Parse and Cgi_Value store query data in the cgi array
11.8 Html_DecodeEntity 11.9 Html_Parse 12.1 Maintaining a tclIndex file 12.2 Loading a tclIndex file 13.1 Calculating clicks per second 13.2 Printing a procedure definition 13.3 Mapping form data onto procedure arguments 13.4 Finding built-in commands 13.5 Getting a trace of the Tcl call stack 13.6 A procedure to read and evaluate commands 13.7 Using info script to find related files 13.8 Tracing variables 13.9 Creating array elements with array traces 13.10 Interactive history usage 13.11 Implementing special history syntax 13.12 A Debug procedure 13.13 Time Stamps in log records 14.1 Random number generator using namespaces 14.2 Random number generator using qualified names 14.3 Nested namespaces 14.4 The code procedure to wrap callbacks 14.5 Listing commands defined by a namespace 15.1 MIME character sets.and file encodings 15.2 Using scripts in nonstandard encodings 15.3 Three sample message catalog files 15.4 Using msgcat::mcunknown to share message catalogs 16.1 A read event file handler 16.2 Using vwait to activate the event loop
16.3 A read event file handler for a nonblocking channel 17.1 Opening a client socket with a timeout 17.2 Opening a server socket 17.3 The echo service 17.4 A client of the echo service 17.5 Opening a connection to an HTTP server 17.6 Opening a connection to an HTTP server 17.7 Http_Head validates a URL 17.8 Using Http_Head 17.9 Http_Get fetches the contents of a URL 17.10 HttpGetText reads text URLs 17.11 HttpCopyDone is used with fcopy 17.12 Downloading files with http::geturl 17.13 Basic Authentication using http::geturl 18.1 A simple URL domain 18.2 Application Direct URLs 18.3 Alternate types for Application Direct URLs 18.4 A sample document type handler 18.5 A one-level site structure 18.6 A HTML + Tcl template file 18.7 SitePage template procedure 18.8 SiteMenu and SiteFooter template procedures 18.9 The SiteLink procedure 18.10 Mail form results with /mail/forminfo 18.11 Mail message sent by /mail/forminfo 18.12 Processing mail sent by /mail/forminfo 18.13 A self-checking form procedure
18.14 A page with a self-checking form 18.15 The /debug/source application-direct URL implementation 19.1 Creating and deleting an interpreter 19.2 Creating a hierarchy of interpreters 19.3 A command alias for exit 19.4 Querying aliases 19.5 Dumping aliases as Tcl commands 19.6 Substitutions and hidden commands 19.7 Opening a file for an unsafe interpreter 19.8 The Safesock security policy 19.9 The Tempfile security policy 19.10 Restricted puts using hidden commands 19.11 A safe after command 21.1 "Hello, World!" Tk program. 21.2 Looking at all widget attributes 22.1 Logging the output of a program run with exec 22.2 A platform-specific cancel event 22.3 A browser for the code examples in the book 22.4 A Tcl shell in a text widget 22.5 Macintosh look and feel 22.6 Windows look and feel 22.7 UNIX look and feel 23.1 Two frames packed inside the main frame 23.2 Turning off geometry propagation 23.3 A horizontal stack inside a vertical stack 23.4 Even more nesting of horizontal and vertical stacks 23.5 Mixing bottom and right packing sides 23.6 Filling the display into extra packing space
23.7 Using horizontal fill in a menu bar 23.8 The effects of internal padding (-ipady) 23.9 Button padding vs. packer padding 23.10 The look of a default button 23.11 Resizing without the expand option 23.12 Resizing with expand turned on 23.13 More than one expanding widget 23.14 Setup for anchor experiments 23.15 The effects of noncenter anchors 23.16 Animating the packing anchors 23.17 Controlling the packing order 23.18 Packing into other relatives 24.1 A basic grid 24.2 A grid with sticky settings 24.3 A grid with row and column specifications 24.4 A grid with external padding 24.5 A grid with internal padding 24.6 All combinations of -sticky settings 24.7 Explicit row and column span 24.8 Grid syntax row and column span 24.9 Row padding compared to widget padding 24.10 Gridding a text widget and scrollbar 25.1 Centering a window with place 25.2 Covering a window with place 25.3 Combining relative and absolute sizes 25.4 Positioning a window above a sibling with place 25.5 Pane_Create sets up vertical or horizontal panes 25.6 PaneDrag adjusts the percentage
25.7 PaneGeometry updates the layout 26.1 Bindings on different binding tags 26.2 Output from the UNIX xmodmap program 26.3 Emacs-like binding convention for Meta and Escape 26.4 Virtual events for cut, copy, and paste 27.1 A troublesome button command 27.2 Fixing the troublesome situation 27.3 A button associated with a Tcl procedure 27.4 Radiobuttons and checkbuttons 27.5 A command on a radiobutton or checkbutton 27.6 A menu sampler 27.7 A menu bar in Tk 8.0 27.8 A simple menu by name package 27.9 Using the Tk 8.0 menu bar facility 27.10 MenuGet maps from name to menu 27.11 Adding menu entries 27.12 A wrapper for cascade entries 27.13 Using the menu by name package 27.14 Keeping the accelerator display up to date 28.1 Reading an option database file 28.2 A file containing resource specifications 28.3 Using resources to specify user-defined buttons 28.4 Resource_ButtonFrame defines buttons based on resources 28.5 Using Resource_ButtonFrame 28.6 Specifying menu entries via resources 28.7 Defining menus from resource specifications 28.8 Resource_GetFamily merges user and application resources 29.1 Macintosh window styles
29.2 A label that displays different strings 29.3 The message widget formats long lines of text 29.4 Controlling the text layout in a message widget 29.5 A scale widget 30.1 A text widget and two scrollbars 30.2 Scroll_Set manages optional scrollbars 30.3 Listbox with optional scrollbars 31.1 A command entry 32.1 Choosing items from a listbox 33.1 Tag configurations for basic character styles 33.2 Line spacing and justification in the text widget 33.3 An active text button 33.4 Delayed creation of embedded widgets 33.5 Using embedded images for a bulleted list 33.6 Finding the current range of a text tag 33.7 Dumping the text widget 33.8 Dumping the text widget with a command callback 34.1 A large scrolling canvas 34.2 The canvas "Hello, World!" example 34.3 A min max scale canvas example 34.4 Moving the markers for the min max scale 34.5 Canvas arc items 34.6 Canvas bitmap items 34.7 Canvas image items 34.8 A canvas stroke drawing example 34.9 Canvas oval items 34.10 Canvas polygon items 34.11 Dragging out a box
34.12 Simple edit bindings for canvas text items 34.13 Using a canvas to scroll a set of widgets 34.14 Generating postscript from a canvas 35.1 Paste the PRIMARY or CLIPBOARD selection 35.2 Separate paste actions 35.3 Bindings for canvas selection 35.4 Selecting objects 35.5 A canvas selection handler 35.6 The copy and cut operations 35.7 Pasting onto the canvas 36.1 Procedures to help build dialogs 36.2 A simple dialog 36.3 A feedback procedure 37.1 Equal-sized labels 37.2 3D relief sampler 37.3 Padding provided by labels and buttons 37.4 Anchoring text in a label or button 37.5 Borders and padding 38.1 Resources for reverse video 38.2 Computing a darker color 38.3 Specifying an image for a widget 38.4 Specifying a bitmap for a widget 38.5 The built-in bitmaps 38.6 The Tk cursors 39.1 The FontWidget procedure handles missing fonts 39.2 Font metrics 39.3 A gridded, resizable listbox
39.4 Font selection dialog 40.1 The sender application 40.2 Hooking the browser to an eval server 40.3 Making the shell into an eval server 40.4 Remote eval using sockets 40.5 Reading commands from a socket 40.6 The client side of remote evaluation 41.1 Gridded geometry for a canvas 41.2 Telling other applications what your name is 42.1 Preferences initialization 42.2 Adding preference items 42.3 Setting preference variables 42.4 Using the preferences package 42.5 A user interface to the preference items 42.6 Interface objects for different preference types 42.7 Displaying the help text for an item 42.8 Saving preferences settings to a file 42.9 Read settings from the preferences file 42.10 Tracing a Tcl variable in a preference item 43.1 A user interface to widget bindings 43.2 Bind_Display presents the bindings for a widget or class 43.3 Related listboxes are configured to select items together 43.4 Controlling a pair of listboxes with one scrollbar 43.5 Drag-scrolling a pair of listboxes together 43.6 An interface to define bindings 43.7 Defining and saving bindings 44.1 The initialization procedure for a loadable package 44.2 The RandomCmd C command procedure
44.3 The RandomObjCmd C command procedure 44.4 The Tcl_Obj structure 44.5 The Plus1ObjCmd procedure 44.6 The Blob and BlobState data structures 44.7 The Blob_Init and BlobCleanup procedures 44.8 The BlobCmd command procedure 44.9 BlobCreate and BlobDelete 44.10 The BlobNames procedure 44.11 The BlobN and BlobData procedures 44.12 The BlobCommand and BlobPoke procedures 44.13 A canonical Tcl main program and Tcl_AppInit 44.14 A canonical Tk main program and Tk_AppInit 44.15 Calling C command procedure directly with Tcl_Invoke 46.1 The Clock_Init procedure 46.2 The Clock widget data structure 46.3 The ClockCmd command procedure 46.4 The ClockObjCmd command procedure 46.5 The ClockInstanceCmd command procedure 46.6 The ClockInstanceObjCmd command procedure 46.7 ClockConfigure allocates resources for the widget 46.8 ClockObjConfigure allocates resources for the widget 46.9 The Tk_ConfigSpec typedef 46.10 Configuration specs for the clock widget 46.11 The Tk_OptionSpec typedef 46.12 The Tk_OptionSpec structure for the clock widget 46.13 ComputeGeometry computes the widget's size 46.14 The ClockDisplay procedure 46.15 The ClockEventPro handles window events
Top
Table of Contents
List of Tables
1-1 Backslash sequences 1-2 Arithmetic operators from highest to lowest precedence 1-3 Built-in math functions 1-4 Built-in Tcl commands 2-1 Wish command line options 2-2 Variables defined by tclsh and wish 3-1 HTML tags used in the examples 4-1 The string command 4-2 Matching characters used with string match 4-3 Character class names 4-4 Format conversions 4-5 Format flags 4-6 Binary conversion types 5-1 List-related commands 8-1 The array command 9-1 Summary of the exec syntax for I/O redirection 9-2 The file command options 9-3 Array elements defined by file stat 9-4 Platform-specific file attributes
9-5 Tcl commands used for file access 9-6 Summary of the open access arguments 9-7 Summary of POSIX flags for the access argument 9-8 The registry command 9-9 The registry data types 11-1 Basic regular expression syntax 11-2 Additional advanced regular expression syntax 11-3 Character classes 11-4 Backslash escapes in regular expressions 11-5 Embedded option characters used with the (?x) syntax 11-6 Options to the regexp command 11-7 Sample regular expressions 12-1 Options to the pkg_mkIndex command 12-2 The package command 13-1 The clock command 13-2 Clock formatting keywords 13-3 UNIX-specific clock formatting keywords 13-4 The info command 13-5 The history command 13-6 Special history syntax 14-1 The namespace command 15-1 The encoding command 15-2 The msgcat package 16-1 The after command 16-2 The fileevent command 16-3 I/O channel properties controlled by fconfigure 16-4 End of line translation modes 17-1 Options to the http::geturl command
17-2 Elements of the http::geturl state array 17-3 The http support procedures 18-1 Httpd support procedures 18-2 Url support procedures 18-3 Doc procedures for configuration 18-4 Doc procedures for generating responses 18-5 Doc procedures that support template processing 18-6 The form package 18-7 Elements of the page array 18-8 Elements of the env array 18-9 Status application-direct URLs 18-10 Debug application-direct URLs 18-11 Application-direct URLS that e-mail form results 18-12 Basic TclHttpd Parameters 19-1 The interp command 19-2 Commands hidden from safe interpreters 19-3 The safe base master interface 19-4 The safe base slave aliases 20-1 Tk commands omitted from safe interpreters 20-2 Plugin Environment Variables 20-3 Aliases defined by the browser package 20-4 The browser::getURL callbacks 21-1 Tk widget-creation commands 21-2 Tk widget-manipulation commands 21-3 Tk support procedures 23-1 The pack command 23-2 Packing options 24-1 The grid command
24-2 Grid widget options 25-1 The place command 25-2 Placement options 26-1 Event types 26-2 Event modifiers 26-3 The event command 26-4 A summary of the event keywords 27-1 Resource names of attributes for all button widgets 27-2 Button operations 27-3 Menu entry index keywords 27-4 Menu operations 27-5 Menu attribute resource names. 27-6 Attributes for menu entries 29-1 Attributes for frame and toplevel widgets 29-2 Label Attributes 29-3 Message Attributes 29-4 Bindings for scale widgets 29-5 ttributes for scale widgets 29-6 perations on the scale widget 30-1 Bindings for the scrollbar widget 30-2 Attributes for the scrollbar widget 30-3 Operations on the scrollbar widget 31-1 Entry bindings 31-2 Entry attribute resource names 31-3 Entry indices 31-4 Entry operations 32-1 Listbox indices 32-2 Listbox operations
32-3 The values for the selectMode of a listbox 32-4 Bindings for browse selection mode 32-5 Bindings for single selection mode 32-6 Bindings for extended selection mode 32-7 Bindings for multiple selection mode 32-8 Listbox scroll bindings 32-9 Listbox attribute resource names 33-1 Text indices 33-2 Index modifiers for text widgets 33-3 Attributes for text tags 33-4 Options to the search operation 33-5 Window and image alignment options 33-6 Options to the window create operation 33-7 Options to the image create operation 33-8 Bindings for the text widget 33-9 Operations for the text widget 33-10 Text attribute resource names 34-1 Arc attributes 34-2 Bitmap attributes 34-3 Image attributes 34-4 Line attributes 34-5 Oval attributes 34-6 Polygon attributes 34-7 Rectangle attributes 34-8 Indices for canvas text items 34-9 Canvas operations that apply to text items 34-10 Text attributes 34-11 Operations on a canvas widget
34-12 Canvas postscript options 34-13 Canvas attribute resource names 35-1 The selection command 35-2 The clipboard command 36-1 Options to tk_messageBox 36-2 Options to the standard file dialogs 36-3 Options to tk_chooseColor 36-4 The focus command 36-5 The grab command 36-6 The tkwait command 37-1 Size attribute resource names 37-2 Border and relief attribute resource names 37-3 Highlight attribute resource names 37-4 Layout attribute resource names 38-1 Color attribute resource names 38-2 Windows system colors 38-3 Macintosh system colors 38-4 Visual classes for displays 38-5 Summary of the image command 38-6 Bitmap image options 38-7 Photo image attributes 38-8 Photo image operations 38-9 Copy options for photo images 38-10 Read options for photo images 38-11 Write options for photo images 38-12 Cursor attribute resource names 39-1 Font attributes
39-2 X Font specification components 39-3 The font command 39-4 Layout attribute resource names 39-5 Selection attribute resource names 40-1 Options to the send command 41-1 Size, placement and decoration window manager operations 41-2 Window manager commands for icons 41-3 Session-related window manager operations 41-4 Miscellaneous window manager operations 41-5 send command information 41-6 Window hierarchy information 41-7 Window size information 41-8 Window location information 41-9 Virtual root window information 41-10 Atom and window ID information 41-11 Colormap and visual class information 45-1 The Tcl source directory structure 45-2 The installation directory structure 45-3 Standard configure flags 45-4 TEA standard Makefile targets 46-1 Configuration flags and corresponding C types 48-1 Changes in color attribute names 52-1 The testthread command 52-2 The dde command options
Top
Table of Contents
Preface
Tcl stands for Tool Command Language. Tcl is really two things: a scripting language, and an interpreter for that language that is designed to be easy to embed into your application. Tcl and its associated graphical user-interface toolkit, Tk, were designed and crafted by Professor John Ousterhout of the University of California, Berkeley. You can find these packages on the Internet (as explained on page lii) and use them freely in your application, even if it is commercial. The Tcl interpreter has been ported from UNIX to DOS, Windows, OS/2, NT, and Macintosh environments. The Tk toolkit has been ported from the X window system to Windows and Macintosh. I first heard about Tcl in 1988 while I was Ousterhout's Ph.D. student at Berkeley. We were designing a network operating system, Sprite. While the students hacked on a new kernel, John wrote a new editor and terminal emulator. He used Tcl as the command language for both tools so that users could define menus and otherwise customize those programs. This was in the days of X10, and he had plans for an X toolkit based on Tcl that would help programs cooperate with each other by communicating with Tcl commands. To me, this cooperation among tools was the essence of Tcl. This early vision imagined that applications would be large bodies of compiled code and a small amount of Tcl used for configuration and high-level commands. John's editor, mx, and the terminal emulator, tx, followed this model. While this model remains valid, it has also turned out to be possible to write entire applications in Tcl. This is because the Tcl/Tk shell, wish, provides access to other programs, the file system, network sockets, plus the ability to create a graphical user interface. For better or worse, it is now common to find applications that contain thousands of lines of Tcl script. This book was written because, while I found it enjoyable and productive to use Tcl and Tk, there were times when I was frustrated. In addition, working at Xerox PARC, with many experts in languages and systems, I was compelled to understand both the strengths and weaknesses of Tcl and Tk. Although many of my colleagues adopted Tcl and Tk for their projects, they were also just as quick to point out its flaws. In response, I have built up a set of programming techniques that exploit the power of Tcl and Tk while avoiding troublesome areas. This book is meant as a practical guide to help you get the most out of Tcl and Tk and avoid some of the frustrations I experienced. It has been about 10 years since I was introduced to Tcl, and about five years since the first edition of this book. During the last several years I have been working under John Ousterhout, first at Sun Microsystems and now at Scriptics Corporation. I have managed to remain mostly a Tcl programmer while others in our group have delved into the C implementation of Tcl itself. I've been building applications like HTML editors, e-mail user interfaces, Web servers, and the customer database we run our business on. This experience is reflected in this book. The bulk of the book is about Tcl scripting,
and the aspects of C programming to create Tcl extensions is given a lighter treatment. I have been lucky to remain involved in the core Tcl development, and I hope I can pass along the insights I have gained by working with Tcl.
Top
Table of Contents
Preface
Why Tcl?
As a scripting language, Tcl is similar to other UNIX shell languages such as the Bourne Shell (sh), the C Shell (csh), the Korn Shell (ksh), and Perl. Shell programs let you execute other programs. They provide enough programmability (variables, control flow, and procedures) to let you build complex scripts that assemble existing programs into a new tool tailored for your needs. Shells are wonderful for automating routine chores. It is the ability to easily add a Tcl interpreter to your application that sets it apart from other shells. Tcl fills the role of an extension language that is used to configure and customize applications. There is no need to invent a command language for your new application, or struggle to provide some sort of userprogrammability for your tool. Instead, by adding a Tcl interpreter, you structure your application as a set of primitive operations that can be composed by a script to best suit the needs of your users. It also allows other programs to have programmatic control over your application, leading to suites of applications that work well together. The Tcl C library has clean interfaces and is simple to use. The library implements the basic interpreter and a set of core scripting commands that implement variables, flow control, and procedures (see page 22). There is also a set of commands that access operating system services to run other programs, access the file system, and use network sockets. Tk adds commands to create graphical user interfaces. Tcl and Tk provide a "virtual machine" that is portable across UNIX, Windows, and Macintosh environments. The Tcl virtual machine is extensible because your application can define new Tcl commands. These commands are associated with a C or C++ procedure that your application provides. The result is applications that are split into a set of primitives written in a compiled language and exported as Tcl commands. A Tcl script is used to compose the primitives into the overall application. The script layer has access to shell-like capability to run other programs, has access to the file system, and can call directly into the compiled part of the application through the Tcl commands you define. In addition, from the C programming level, you can call Tcl scripts, set and query Tcl variables, and even trace the execution of the Tcl interpreter. There are many Tcl extensions freely available on the Internet. Most extensions include a C library that provides some new functionality, and a Tcl interface to the library. Examples include database access, telephone control, MIDI controller access, and expect, which adds Tcl commands to control interactive programs.
The most notable extension is Tk, a toolkit for graphical user interfaces. Tk defines Tcl commands that let you create and manipulate user interface widgets. The script-based approach to user interface programming has three benefits: Development is fast because of the rapid turnaround; there is no waiting for long compilations. The Tcl commands provide a higher-level interface than most standard C library user-interface toolkits. Simple user interfaces require just a handful of commands to define them. At the same time, it is possible to refine the user interface in order to get every detail just so. The fast turnaround aids the refinement process. The user interface can be factored out from the rest of your application. The developer can concentrate on the implementation of the application core and then fairly painlessly work up a user interface. The core set of Tk widgets is often sufficient for all your user interface needs. However, it is also possible to write custom Tk widgets in C, and again there are many contributed Tk widgets available on the network. There are other choices for extension languages that include Visual Basic, Scheme, Elisp, Perl, Python, and Javascript. Your choice between them is partly a matter of taste. Tcl has simple constructs and looks somewhat like C. It is easy to add new Tcl primitives by writing C procedures. Tcl is very easy to learn, and I have heard many great stories of users completing impressive projects in a short amount of time (e.g., a few weeks), even though they never used Tcl before. Java has exploded onto the computer scene since this book was first published. Java is a great systems programming language that in the long run could displace C and C++. This is fine for Tcl, which is designed to glue together building blocks written in any system programming language. Tcl was designed to work with C, but has been adapted to work with the Java Virtual Machine. Where I say "C or C++", you can now say "C, C++, or Java," but the details are a bit different with Java. This book does not describe the Tcl/Java interface, but you can find TclBlend on the CD-ROM. TclBlend loads the Java Virtual Machine into your Tcl application and lets you invoke Java methods. It also lets you implement Tcl commands in Java instead of C or C++. Javascript is a language from Netscape that is designed to script interactions with Web pages. Javascript is important because Netscape is widely deployed. However, Tcl provides a more general purpose scripting solution that can be used in a wide variety of applications. The Tcl/Tk Web browser plugin provides a way to run Tcl in your browser. It turns out to be more of a Java alternative than a JavaScript alternative. The plugin lets you run Tcl applications inside your browser, while JavaScript gives you fine grain control over the browser and HTML display. The plugin is described in Chapter 20.
Top
Table of Contents
Preface
Tcl/Tk 8.1 features full Unicode support, a new regular expression engine that provides all the features found in Perl 5, and thread safety so that you can embed Tcl into multithreaded applications. Tk does a heroic job of finding the correct font to display your Unicode characters, and it adds a message catalog facility so that you can write internationalized applications. The release history of Tcl/Tk 8.1 also straddled the Sun to Scriptics transition. The first alpha release was made in the fall of 1997, and the final patch release, 8.1.1, was made in May 1999. Tcl/Tk 8.2 is primarily a bug fix and stabilization release. There are a few minor additions to the Tcl C library APIs to support more extensions without requiring core patches. Tcl/Tk 8.2 went rapidly into final release in the summer of 1999.
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Tcl/Tk in a Nutshell (O'Reilly, 1999) by Paul Raines and Jeff Tranter is a handy reference guide. It covers several popular extensions including Expect, [incr Tcl], Tix, TclX, BLT, SybTcl, OraTcl, and TclODBC. There is a tiny pocket-reference guide for Tcl/Tk that may eliminate the need to thumb through my large book to find the syntax of a particular Tcl or Tk command. Web Tcl Complete (McGraw Hill, 1999) by Steve Ball describes programming with the Tcl Web Server. It also covers Tcl/Java integration using TclBlend. [incr Tcl] From The Ground Up (Osborn-McGraw Hill, 1999) by Chad Smith describes the [incr Tcl] object-oriented extension to Tcl.
Top
Table of Contents
Preface
On-line Examples
The book comes with a CD-ROM that has source code for all of the examples, plus a selection of Tcl freeware found on the Internet. The CD-ROM is created with the Linux mkhybrid program, so it is readable on UNIX, Windows, and Macintosh. There, you will find the versions of Tcl and Tk that were available as the book went to press. You can also retrieve the sources shown in the book from my personal Web site: http://www.beedub.com/book/
Top
Table of Contents
Preface
Ftp Archives
The primary site for the Tcl and Tk distributions is given below as a Universal Resource Location (URL): ftp://ftp.scriptics.com/pub/tcl You can use FTP and log in to the host (e.g., ftp.scriptics.com) under the anonymous user name. Give your e-mail address as the password. The directory is in the URL after the host name (e.g., /pub/tcl). There are many sites that mirror this distribution. The mirror sites provide an archive site for contributed Tcl commands, Tk widgets, and applications. There is also a set of Frequently Asked Questions files. These are some of the sites that maintain Tcl archives ftp://ftp.neosoft.com/pub/tcl ftp://ftp.syd.dit.csiro.au/pub/tk ftp://ftp.ibp.fr/pub/tcl ftp://src.doc.ic.ac.uk/packages/tcl/ ftp://ftp.luth.se/pub/unix/tcl/ ftp://sunsite.cnlab-switch.ch/mirror/tcl ftp://ftp.sterling.com/programming/languages/tcl ftp://ftp.sunet.se/pub/lang/tcl ftp://ftp.cs.columbia.edu/archives/tcl ftp://ftp.uni-paderborn.de/pub/unix/tcl ftp://sunsite.unc.edu/pub/languages/tcl ftp://ftp.funet.fi/pub/languages/tcl You can use a World Wide Web browser like Mosaic, Netscape, Internet Explorer, or Lynx to access
these sites. Enter the URL as specified above, and you are presented with a directory listing of that location. From there you can change directories and fetch files. If you do not have direct FTP access, you can use an e-mail server for FTP. Send e-mail to ftpmail@decwrl.dec.com with the message Help to get directions. If you are on BITNET, send e-mail to bitftp@pucc.princeton.edu. You can search for FTP sites that have Tcl by using the Archie service that indexes the contents of anonymous FTP servers. Information about using Archie can be obtained by sending mail to archie@archie.sura.net that contains the message Help.
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Newsgroups
The comp.lang.tcl newsgroup is very active. It provides a forum for questions and answers about Tcl. Announcements about Tcl extensions and applications are posted to the comp.lang.tcl.announce newsgroup.
Top
Table of Contents
Preface
Typographic Conventions
The more important examples are set apart with a title and horizontal rules, while others appear inline. The examples use courier for Tcl and C code. When interesting results are returned by a Tcl command, those are presented below in oblique courier. The => is not part of the return value in the following example. expr 5 + 8 => 13 The courier font is also used when naming Tcl commands and C procedures within sentences. The usage of a Tcl command is presented in the following example. The command name and constant keywords appear in courier. Variable values appear in courier oblique. Optional arguments are surrounded with question marks. set varname ?value? The name of a program is in italics: xterm
Top
Table of Contents
Preface
Hot Tips
The icon in the margin marks a "hot tip" as judged by the reviewers of the book. The visual markers help you locate the more useful sections in the book. These are also listed in the index under Hot Tip.
Top
Table of Contents
Preface
Book Organization
The chapters of the book are divided into seven parts. The first part describes basic Tcl features. The first chapter describes the fundamental mechanisms that characterize the Tcl language. This is an important chapter that provides the basic grounding you will need to use Tcl effectively. Even if you have programmed in Tcl already, you should review Chapter 1. Chapter 2 goes over the details of using Tcl and Tk on UNIX, Windows, and Macintosh. Chapter 3 presents a sample application, a CGI script, that illustrates typical Tcl programming. The rest of Part I covers the basic Tcl commands in more detail, including string handling, data types, control flow, procedures, and scoping issues. Part I finishes with a description of the facilities for file I/O and running other programs. Part II describes advanced Tcl programming. It starts with eval, which lets you generate Tcl programs on the fly. Regular expressions provide powerful string processing. If your data-processing application runs slowly, you can probably boost its performance significantly with the regular expression facilities. Namespaces partition the global scope of procedures and variables. Unicode and message catalogs support internationalized applications. Libraries and packages provide a way to organize your code for sharing among projects. The introspection facilities of Tcl tell you about the internal state of Tcl. Event driven I/O helps server applications manage several clients simultaneously. Network sockets are used to implement the HTTP protocol used to fetch pages on the World Wide Web. Safe-Tcl is used to provide a secure environment to execute applets downloaded over the network. TclHttpd is an extensible web server built in Tcl. You can build applications on top of this server, or embed it into your existing applications to give them a web interface. Part III introduces Tk. It gives an overview of the toolkit facilities. A few complete examples are examined in detail to illustrate the features of Tk. Event bindings associate Tcl commands with events like keystrokes and button clicks. Part III ends with three chapters on the Tk geometry managers that provide powerful facilities for organizing your user interface. Part IV describes the Tk widgets. These include buttons, menus, scrollbars, labels, text entries, multiline and multifont text areas, drawing canvases, listboxes, and scales. The Tk widgets are highly configurable and very programmable, but their default behaviors make them easy to use as well. The resource database that can configure widgets provides an easy way to control the overall look of your application. Part V describes the rest of the Tk facilities. These include selections, keyboard focus, and standard dialogs. Fonts, colors, images, and other attributes that are common to the Tk widgets are described in detail. This part ends with a few larger Tk examples.
Part VI is an introduction to C programming and Tcl. The goal of this part is to get you started in the right direction when you need to extend Tcl with new commands written in C or integrate Tcl into custom applications. Part VII provides a chapter for each of the Tcl/Tk releases covered by the book. These chapters provide details about what features were changed and added. They also provide a quick reference if you need to update a program or start to use a new version.
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Top
Table of Contents
Preface
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Tcl Commands
Tcl stands for Tool Command Language. A command does something for you, like output a string, compute a math expression, or display a widget on the screen. Tcl casts everything into the mold of a command, even programming constructs like variable assignment and procedure definition. Tcl adds a tiny amount of syntax needed to properly invoke commands, and then it leaves all the hard work up to the command implementation. The basic syntax for a Tcl command is: command arg1 arg2 arg3 ... The command is either the name of a built-in command or a Tcl procedure. White space (i.e., spaces or tabs) is used to separate the command name and its arguments, and a newline (i.e., the end of line character) or semicolon is used to terminate a command. Tcl does not interpret the arguments to the commands except to perform grouping, which allows multiple words in one argument, and substitution, which is used with programming variables and nested command calls. The behavior of the Tcl command processor can be summarized in three basic steps:
Argument grouping. Value substitution of nested commands, variables, and backslash escapes. Command invocation. It is up to the command to interpret its arguments. This model is described in detail in this Chapter.
Top
Table of Contents
Hello, World!
Example 1-1 The "Hello, World!" example. puts stdout {Hello, World!} => Hello, World! In this example, the command is puts, which takes two arguments: an I/O stream identifier and a string. puts writes the string to the I/O stream along with a trailing newline character. There are two points to emphasize: Arguments are interpreted by the command. In the example, stdout is used to identify the standard output stream. The use of stdout as a name is a convention employed by puts and the other I/O commands. Also, stderr is used to identify the standard error output, and stdin is used to identify the standard input. Chapter 9 describes how to open other files for I/O. Curly braces are used to group words together into a single argument. The puts command receives Hello, World! as its second argument. The braces are not part of the value.
The braces are syntax for the interpreter, and they get stripped off before the value is passed to the command. Braces group all characters, including newlines and nested braces, until a matching brace is found. Tcl also uses double quotes for grouping. Grouping arguments will be described in more detail later.
Top
Table of Contents
Variables
The set command is used to assign a value to a variable. It takes two arguments: The first is the name of the variable, and the second is the value. Variable names can be any length, and case is significant. In fact, you can use any character in a variable name. It is not necessary to declare Tcl variables before you use them.
The interpreter will create the variable when it is first assigned a value. The value of a variable is obtained later with the dollar-sign syntax, illustrated in Example 1-2: Example 1-2 Tcl variables. set var 5 => 5 set b $var => 5 The second set command assigns to variable b the value of variable var. The use of the dollar sign is our first example of substitution. You can imagine that the second set command gets rewritten by substituting the value of var for $var to obtain a new command. set b 5 The actual implementation of substitution is more efficient, which is important when the value is large.
Top
Table of Contents
Command Substitution
The second form of substitution is command substitution. A nested command is delimited by square brackets, [ ]. The Tcl interpreter takes everything between the brackets and evaluates it as a command. It rewrites the outer command by replacing the square brackets and everything between them with the result of the nested command. This is similar to the use of backquotes in other shells, except that it has the additional advantage of supporting arbitrary nesting of commands. Example 1-3 Command substitution. set len [string length foobar] => 6 In Example 1-3, the nested command is: string length foobar This command returns the length of the string foobar. The string command is described in detail starting on page 45. The nested command runs first. Then, command substitution causes the outer command to be rewritten as if it were: set len 6 If there are several cases of command substitution within a single command, the interpreter processes them from left to right. As each right bracket is encountered, the command it delimits is evaluated. This results in a sensible ordering in which nested commands are evaluated first so that their result can be used in arguments to the outer command.
Top
Table of Contents
Math Expressions
The Tcl interpreter itself does not evaluate math expressions. Tcl just does grouping, substitutions and command invocations. The expr command is used to parse and evaluate math expressions. Example 1-4 Simple arithmetic. expr 7.2 / 4 => 1.8 The math syntax supported by expr is the same as the C expression syntax. The expr command deals with integer, floating point, and boolean values. Logical operations return either 0 (false) or 1 (true). Integer values are promoted to floating point values as needed. Octal values are indicated by a leading zero (e.g., 033 is 27 decimal). Hexadecimal values are indicated by a leading 0x. Scientific notation for floating point numbers is supported. A summary of the operator precedence is given on page 20. You can include variable references and nested commands in math expressions. The following example uses expr to add the value of x to the length of the string foobar. As a result of the innermost command substitution, the expr command sees 6 + 7, and len gets the value 13: Example 1-5 Nested commands. set x 7 set len [expr [string length foobar] + $x] => 13 The expression evaluator supports a number of built-in math functions. (For a complete listing, see page 21.) Example 1-6 computes the value of pi: Example 1-6 Built-in math functions.
set pi [expr 2*asin(1.0)] => 3.1415926535897931 The implementation of expr is careful to preserve accurate numeric values and avoid conversions between numbers and strings. However, you can make expr operate more efficiently by grouping the entire expression in curly braces. The explanation has to do with the byte code compiler that Tcl uses internally, and its effects are explained in more detail on page 15. For now, you should be aware that these expressions are all valid and run a bit faster than the examples shown above: Example 1-7 Grouping expressions with braces. expr {7.2 / 4} set len [expr {[string length foobar] + $x}] set pi [expr {2*asin(1.0)}]
Top
Table of Contents
Backslash Substitution
The final type of substitution done by the Tcl interpreter is backslash substitution. This is used to quote characters that have special meaning to the interpreter. For example, you can specify a literal dollar sign, brace, or bracket by quoting it with a backslash. As a rule, however, if you find yourself using lots of backslashes, there is probably a simpler way to achieve the effect you are striving for. In particular, the list command described on page 61 will do quoting for you automatically. In Example 1-8 backslash is used to get a literal $: Example 1-8 Quoting special characters with backslash. set dollar \$foo => $foo set x $dollar => $foo Only a single round of interpretation is done.
The second set command in the example illustrates an important property of Tcl. The value of dollar does not affect the substitution performed in the assignment to x. In other words, the Tcl parser does not care about the value of a variable when it does the substitution. In the example, the value of x and dollar is the string $foo. In general, you do not have to worry about the value of variables until you use eval, which is described in Chapter 10. You can also use backslash sequences to specify characters with their Unicode, hexadecimal, or octal value: set escape \u001b
set escape \0x1b set escape \033 The value of variable escape is the ASCII ESC character, which has character code 27. The table on page 20 summarizes backslash substitutions. A common use of backslashes is to continue long commands on multiple lines. This is necessary because a newline terminates a command. The backslash in the next example is required; otherwise the expr command gets terminated by the newline after the plus sign. Example 1-9 Continuing long lines with backslashes. set totalLength [expr [string length $one] + \ [string length $two]] There are two fine points to escaping newlines. First, if you are grouping an argument as described in the next section, then you do not need to escape newlines; the newlines are automatically part of the group and do not terminate the command. Second, a backslash as the last character in a line is converted into a space, and all the white space at the beginning of the next line is replaced by this substitution. In other words, the backslash-newline sequence also consumes all the leading white space on the next line.
Top
Table of Contents
length of $s is [string length $s]." Hello is 5. length of $s is [string length $s].} $s is [string length $s].
In the second command of Example 1-10, the Tcl interpreter does variable and command substitution on the second argument to puts. In the third command, substitutions are prevented, so the string is printed as is. In practice, grouping with curly braces is used when substitutions on the argument must be delayed until a later time (or never done at all). Examples include loops, conditional statements, and procedure declarations. Double quotes are useful in simple cases like the puts command previously shown. Another common use of quotes is with the format command. This is similar to the C printf function. The first argument to format is a format specifier that often includes special characters like newlines, tabs, and spaces. The easiest way to specify these characters is with backslash sequences (e.g., \n for newline and \t for tab). The backslashes must be substituted before the format command is called, so you need to use quotes to group the format specifier. puts [format "Item: %s\t%5.3f" $name $value] Here format is used to align a name and a value with a tab. The %s and %5.3f indicate how the
remaining arguments to format are to be formatted. Note that the trailing \n usually found in a C printf call is not needed because puts provides one for us. For more information about the format command, see page 52.
The white space inside the nested command is ignored for the purposes of grouping the argument. By the time Tcl encounters the left bracket, it has already done some variable substitutions to obtain: 7+9= When the left bracket is encountered, the interpreter calls itself recursively to evaluate the nested command. Again, the $x and $y are substituted before calling expr. Finally, the result of expr is substituted for everything from the left bracket to the right bracket. The puts command gets the following as its second argument: 7+9=16 Grouping before substitution.
The point of this example is that the grouping decision about puts's second argument is made before the command substitution is done. Even if the result of the nested command contained spaces or other special characters, they would be ignored for the purposes of grouping the arguments to the outer command. Grouping and variable substitution interact the same as grouping and command substitution. Spaces or special characters in variable values do not affect grouping decisions because these decisions are made before the variable values are substituted. If you want the output to look nicer in the example, with spaces around the + and =, then you must use double quotes to explicitly group the argument to puts: puts stdout "$x + $y = [expr $x + $y]" The double quotes are used for grouping in this case to allow the variable and command substitution on the argument to puts.
Top
Table of Contents
Procedures
Tcl uses the proc command to define procedures. Once defined, a Tcl procedure is used just like any of the other built-in Tcl commands. The basic syntax to define a procedure is: proc name arglist body The first argument is the name of the procedure being defined. The second argument is a list of parameters to the procedure. The third argument is a command body that is one or more Tcl commands. The procedure name is case sensitive, and in fact it can contain any characters. Procedure names and variable names do not conflict with each other. As a convention, this book begins procedure names with uppercase letters and it begins variable names with lowercase letters. Good programming style is important as your Tcl scripts get larger. Tcl coding style is discussed in Chapter 12. Example 1-12 Defining a procedure. proc Diag {a b} { set c [expr sqrt($a * $a + $b * $b)] return $c } puts "The diagonal of a 3, 4 right triangle is [Diag 3 4]" => The diagonal of a 3, 4 right triangle is 5.0 The Diag procedure defined in the example computes the length of the diagonal side of a right triangle given the lengths of the other two sides. The sqrt function is one of many math functions supported by the expr command. The variable c is local to the procedure; it is defined only during execution of Diag. Variable scope is discussed further in Chapter 7. It is not really necessary to use the variable c in this example. The procedure can also be written as:
proc Diag {a b} { return [expr sqrt($a * $a + $b * $b)] } The return command is used to return the result of the procedure. The return command is optional in this example because the Tcl interpreter returns the value of the last command in the body as the value of the procedure. So, the procedure could be reduced to: proc Diag {a b} { expr sqrt($a * $a + $b * $b) } Note the stylized use of curly braces in the example. The curly brace at the end of the first line starts the third argument to proc, which is the command body. In this case, the Tcl interpreter sees the opening left brace, causing it to ignore newline characters and scan the text until a matching right brace is found. Double quotes have the same property. They group characters, including newlines, until another double quote is found. The result of the grouping is that the third argument to proc is a sequence of commands. When they are evaluated later, the embedded newlines will terminate each command. The other crucial effect of the curly braces around the procedure body is to delay any substitutions in the body until the time the procedure is called. For example, the variables a, b, and c are not defined until the procedure is called, so we do not want to do variable substitution at the time Diag is defined. The proc command supports additional features such as having variable numbers of arguments and default values for arguments. These are described in detail in Chapter 7.
Top
Table of Contents
A Factorial Example
To reinforce what we have learned so far, below is a longer example that uses a while loop to compute the factorial function: Example 1-13 A while loop to compute factorial. proc Factorial {x} { set i 1; set product 1 while {$i <= $x} { set product [expr $product * $i] incr i } return $product } Factorial 10 => 3628800 The semicolon is used on the first line to remind you that it is a command terminator just like the newline character. The while loop is used to multiply all the numbers from one up to the value of x. The first argument to while is a boolean expression, and its second argument is a command body to execute. The while/ command and other control structures are described in Chapter 6. The same math expression evaluator used by the expr command is used by while to evaluate the boolean expression. There is no need to explicitly use the expr command in the first argument to while, even if you have a much more complex expression. The loop body and the procedure body are grouped with curly braces in the same way. The opening curly brace must be on the same line as proc and while. If you like to put opening curly braces on the line after a while or if statement, you must escape the newline with a backslash:
while {$i < $x}\ { set product ... } Always group expressions and command bodies with curly braces.
Curly braces around the boolean expression are crucial because they delay variable substitution until the while command implementation tests the expression. The following example is an infinite loop: set i 1; while $i<=10 {incr i} The loop will run indefinitely.[*] The reason is that the Tcl interpreter will substitute for $i before while is called, so while gets a constant expression 1<=10 that will always be true. You can avoid these kinds of errors by adopting a consistent coding style that groups expressions with curly braces:
[*] Ironically,
Tcl 8.0 introduced a byte-code compiler, and the first releases of Tcl 8.0 had a bug in the compiler that caused this loop to terminate! This bug is fixed in the 8.0.5 patch release.
set i 1; while {$i<=10} {incr i} The incr command is used to increment the value of the loop variable i. This is a handy command that saves us from the longer command: set i [expr $i + 1] The incr command can take an additional argument, a positive or negative integer by which to change the value of the variable. Using this form, it is possible to eliminate the loop variable i and just modify the parameter x. The loop body can be written like this: while {$x > 1} { set product [expr $product * $x] incr x -1 } Example 1-14 shows factorial again, this time using a recursive definition. A recursive function is one that calls itself to complete its work. Each recursive call decrements x by one, and when x is one, then the recursion stops.
Example 1-14 A recursive definition of factorial. proc Factorial {x} { if {$x <= 1} { return 1 } else { return [expr $x * [Factorial [expr $x - 1]]] } }
Top
Table of Contents
The Tcl interpreter makes some assumptions about variable names that make it easy to embed variable references into other strings. By default, it assumes that variable names contain only letters, digits, and the underscore. The construct $foo.o represents a concatenation of the value of foo and the literal ".o". If the variable reference is not delimited by punctuation or white space, then you can use curly braces to explicitly delimit the variable name (e.g., ${x}). You can also use this to reference variables with funny characters in their name, although you probably do not want variables named like that. If you find yourself using funny variable names, or computing the names of variables, then you may want to use the upvar command. Example 1-16 Embedded variable references. set foo filename set object $foo.o => filename.o set a AAA set b abc${a}def => abcAAAdef set .o yuk! set x ${.o}y => yuk!y
Example 7-6 on page 86 implements a new version of incr which handles this case.
Top
Table of Contents
However, the string compare and string equal commands described in Chapter 4 are more reliable because expr may do conversions on strings that look like numbers. The issues with string operations and expr are discussed on page 48. Expressions can include variable and command substitutions and still be grouped with curly braces. This is because an argument to expr is subject to two rounds of substitution: one by the Tcl interpreter, and a second by expr itself. Ordinarily this is not a problem because math values do not contain the characters that are special to the Tcl interpreter. The second round of substitutions is needed to support commands like while and if that use the expression evaluator internally. Grouping expressions can make them run more efficiently.
You should always group expressions in curly braces and let expr do command and variable substitutions. Otherwise, your values may suffer extra conversions from numbers to strings and back to numbers. Not only is this process slow, but the conversions can loose precision in certain circumstances. For example, suppose x is computed from a math function: set x [expr {sqrt(2.0)}] At this point the value of x is a double-precision floating point value, just as you would expect. If you do this: set two [expr $x * $x] then you may or may not get 2.0 as the result! This is because Tcl will substitute $x and expr will concatenate all its arguments into one string, and then parse the expression again. In contrast, if you do this: set two [expr {$x * $x}] then expr will do the substitutions, and it will be careful to preserve the floating point value of x. The expression will be more accurate and run more efficiently because no string conversions will be done. The story behind Tcl values is described in more detail in Chapter 44 on C programming and Tcl.
Top
Table of Contents
Comments
Tcl uses the pound character, #, for comments. Unlike in many other languages, the # must occur at the beginning of a command. A # that occurs elsewhere is not treated specially. An easy trick to append a comment to the end of a command is to precede the # with a semicolon to terminate the previous command: # Here are some parameters set rate 7.0 ;# The interest rate set months 60 ;# The loan term One subtle effect to watch for is that a backslash effectively continues a comment line onto the next line of the script. In addition, a semicolon inside a comment is not significant. Only a newline terminates comments: # Here is the start of a Tcl comment \ and some more of it; still in the comment The behavior of a backslash in comments is pretty obscure, but it can be exploited as shown in Example 2-3 on page 27. A surprising property of Tcl comments is that curly braces inside comments are still counted for the purposes of finding matching brackets. I think the motivation for this mis-feature was to keep the original Tcl parser simpler. However, it means that the following will not work as expected to comment out an alternate version of an if expression: # if {boolean expression1} { if {boolean expression2} { some commands }
The previous sequence results in an extra left curly brace, and probably a complaint about a missing close brace at the end of your script! A technique I use to comment out large chunks of code is to put the code inside an if block that will never execute: if {0} { unused code here }
Top
Table of Contents
A single round of substitutions is performed before command invocation. The result of a substitution is not interpreted a second time. This rule is important if you have a variable value or a command result that contains special characters such as spaces, dollar signs, square brackets, or braces. Because only a single round of substitution is done, you do not have to worry about special characters in values causing extra substitutions.
Top
Table of Contents
Fine Points
A common error is to forget a space between arguments when grouping with braces or quotes. This is because white space is used as the separator, while the braces or quotes only provide grouping. If you forget the space, you will get syntax errors about unexpected characters after the closing brace or quote. The following is an error because of the missing space between } and {: if {$x > 1} {puts "x = $x"} A double quote is only used for grouping when it comes after white space. This means you can include a double quote in the middle of a group without quoting it with a backslash. This requires that curly braces or white space delimit the group. I do not recommend using this obscure feature, but this is what it looks like: set silly a"b When double quotes are used for grouping, the special effect of curly braces is turned off. Substitutions occur everywhere inside a group formed with double quotes. In the next command, the variables are still substituted: set x xvalue set y "foo {$x}bar" => foo {xvalue}bar When double quotes are used for grouping and a nested command is encountered, the nested command can use double quotes for grouping, too. puts "results [format "%f %f" $x $y]" Spaces are not required around the square brackets used for command substitution. For the purposes of grouping, the interpreter considers everything between the square brackets as part of
the current group. The following sets x to the concatenation of two command results because there is no space between ] and [. set x [cmd1][cmd2] Newlines and semicolons are ignored when grouping with braces or double quotes. They get included in the group of characters just like all the others. The following sets x to a string that contains newlines: set x "This is line one. This is line two. This is line three." During command substitution, newlines and semicolons are significant as command terminators. If you have a long command that is nested in square brackets, put a backslash before the newline if you want to continue the command on another line. This was illustrated in Example 1-9 on page 8. A dollar sign followed by something other than a letter, digit, underscore, or left parenthesis is treated as a literal dollar sign. The following sets x to the single character $. set x $
Top
Table of Contents
Reference
Backslash Sequences
Bell. (0x7) Backspace. (0x8) Form feed. (0xc) Newline. (0xa) Carriage return. (0xd) Tab. (0x9) Vertical tab. (0xb) Replace the newline and the leading white space on the next line with a space. Backslash. ('\') Octal specification of character code. 1, 2, or 3 digits. Hexadecimal specification of character code. 1 or 2 digits. Hexadecimal specification of a 16-bit Unicode character value. 4 hex digits. Replaced with literal c if c is not one of the cases listed above. In particular, \$, \", \{, \} , \] , and \[ are used to obtain these characters.
Arithmetic Operators
Unary minus, bitwise NOT, logical NOT. Multiply, divide, remainder. Add, subtract. Left shift, right shift. Comparison: less, greater, less or equal, greater or equal. Equal, not equal. Bitwise AND. Bitwise XOR. Bitwise OR. Logical AND. Logical OR. If x then y else z.
Arc cosine of x. Arc sine of x. Arc tangent of x. Rectangular (x,y) to polar (r,th). atan2 gives th. Least integral value greater than or equal to x. Cosine of x. Hyperbolic cosine of x. Exponential, ex. Greatest integral value less than or equal to x. Floating point remainder of x/y. Returns sqrt(x*x + y*y). r part of polar coordinates. Natural log of x. Log base 10 of x.
x
sin(x) sinh(x) sqrt(x) tan(x) tanh(x) abs(x) double(x) int(x) round(x) rand() srand(x)
Sine of x. Hyperbolic sine of x. Square root of x. Tangent of x. Hyperbolic tangent of x. Absolute value of x. Promote x to floating point. Truncate x to an integer. Round x to an integer. Return a random floating point value between 0.0 and 1.0. Set the seed for the random number generator to the integer x.
Pg. Description 218 Schedule a Tcl command for later execution. 51 Append arguments to a variable's value. No spaces added. 91 Query array state and search through elements. 54 Convert between strings and binary data. 77 Exit loop prematurely. 77 Trap errors. 115 Change working directory. 173 Get the time and format date strings. 115 Close an open I/O stream. 61 Concatenate arguments with spaces between. Splices lists. 28 Control the console used to enter commands interactively. 77 Continue with next loop iteration. 79 Raise an error. 109 Check for end of file.
Command eval exec exit expr fblocked fconfigure fcopy file fileevent flush for foreach format gets glob global history if incr info interp join lappend lindex linsert list llength load lrange lreplace lsearch
Pg. Description 122 Concatenate arguments and evaluate them as a command. 99 Fork and execute a UNIX program. 116 Terminate the process. 6 Evaluate a math expression. 223 Poll an I/O channel to see if data is ready. 221 Set and query I/O channel properties. 239 Copy from one I/O channel to another. 102 Query the file system. 219 Register callback for event-driven I/O. 109 Flush output from an I/O stream's internal buffers. 76 Loop construct similar to C for statement. 73 Loop construct over a list, or lists, of values. 52 Format a string similar to C sprintf. 112 Read a line of input from an I/O stream. 115 Expand a pattern to matching file names. 84 Declare global variables. 185 Use command-line history. 70 Conditional command. Allows else and elseif clauses. 12 Increment a variable by an integer amount. 176 Query the state of the Tcl interpreter. 276 Create additional Tcl interpreters. 65 Concatenate list elements with a given separator string. 61 Add elements to the end of a list. 63 Fetch an element of a list. 64 Insert elements into a list. 61 Create a list out of the arguments. 63 Return the number of elements in a list. 609 Load shared libraries that define Tcl commands. 63 Return a range of list elements. 64 Replace elements of a list. 64 Search for an element of a list that matches a pattern.
Command lsort namespace open package pid proc puts pwd read regexp regsub rename return scan seek set socket source split string subst switch tell time trace unknown unset uplevel upvar variable vwait
Pg. Description 65 Sort a list. 203 Create and manipulate namespaces. 110 Open a file or process pipeline for I/O. 165 Provide or require code packages. 116 Return the process ID. 81 Define a Tcl procedure. 112 Output a string to an I/O stream. 115 Return the current working directory. 113 Read blocks of characters from an I/O stream. 148 Match regular expressions. 152 Substitute based on regular expressions. 82 Change the name of a Tcl command. 80 Return a value from a procedure. 54 Parse a string according to a format specification. 114 Set the seek offset of an I/O stream. 5 Assign a value to a variable. 228 Open a TCP/IP network connection. 26 Evaluate the Tcl commands in a file. 65 Chop a string up into list elements. 45 Operate on strings. 132 Substitute embedded commands and variable references. 71 Multi-way branch. 114 Return the current seek offset of an I/O stream. 191 Measure the execution time of a command. 183 Monitor variable assignments. 167 Handle unknown commands. 13 Delete variables. 130 Execute a command in a different scope. 85 Reference a variable in a different scope. 197 Declare namespace variables. 220 Wait for a variable to be modified.
Command while
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
On most UNIX systems, this special first line is limited to 32 characters, including the #!. If the pathname is too long, you may end up with /bin/sh trying to interpret your script, giving you syntax errors. You might try using a symbolic link from a short name to the true, long name of the interpreter. However, watch out for systems like Solaris in which the script interpreter cannot be a symbolic link. Fortunately, Solaris doesn't impose a 32-character limit on the pathname, so you can just use a long pathname. The next example shows a trick that works around the pathname length limitation in all cases. The trick comes from a posting to comp.lang.tcl by Kevin Kenny. It takes advantage of a difference between comments in Tcl and the Bourne shell. Tcl comments are described on page 16. In the example, the Bourne shell command that runs the Tcl interpreter is hidden in a comment as far as Tcl is concerned, but it is visible to /bin/sh: Example 2-3 Using /bin/sh to run a Tcl script. #!/bin/sh # The backslash makes the next line a comment in Tcl \ exec /some/very/long/path/to/wish "$0" ${1+"$@"} # ... Tcl script goes here ... You do not even have to know the complete pathname of tclsh or wish to use this trick. You can just do the following: #!/bin/sh # Run wish from the users PATH \ exec wish -f "$0" ${1+"$@"} The drawback of an incomplete pathname is that many sites have different versions of wish and tclsh that correspond to different versions of Tcl and Tk. In addition, some users may not have these programs in their PATH. If you have Tk version 3.6 or earlier, its version of wish requires a -f argument to make it read the contents of a file. The -f switch is ignored in Tk 4.0 and higher versions. The -f, if required, is also counted in the 32-character limit on #! lines. #!/usr/local/bin/wish -f
Top
Table of Contents
Top
Table of Contents
First, make a copy of Wish and open the copy in ResEdit. Pull down the Resource menu and select Create New Resource operation to make a new TEXT resource. ResEdit opens a window and you can type in text. Type in a source command that names your script: source "Hard Disk:Tcl/Tk 8.1:Applications:MyScript.tcl" Set the name of the resource to be tclshrc. You do this through the Get Resource Info dialog under the Resources menu in ResEdit. This sequence of commands is captured in an application called "Drag n Drop Tclets", which comes with the Macintosh Tcl distribution. If you drag a Tcl script onto this icon, it will create a copy of Wish and create the tclshrc text resource that has a source command that will load that script. If you have a Macintosh development environment, you can build a version of Wish that has additional resources built right in. You add the resources to the applicationInit.r file. If a resource contains Tcl code, you use it like this: source -rcrc resource If you don't want to edit resources, you can just use the Wish Source menu to select a script to run.
Top
Table of Contents
Top
Table of Contents
Command-Line Arguments
If you run a script from the command line, for example from a UNIX shell, you can pass the script command-line arguments. You can also specify these arguments in the shortcut command in Windows. For example, under UNIX you can type this at a shell: % myscript.tcl arg1 arg2 arg3 In Windows, you can have a shortcut that runs wish on your script and also passes additional arguments: "c:\Program Files\TCL82\wish.exe" c:\your\script.tcl arg1 The Tcl shells pass the command-line arguments to the script as the value of the argv variable. The number of command-line arguments is given by the argc variable. The name of the program, or script, is not part of argv nor is it counted by argc. Instead, it is put into the argv0 variable. Table 2-2 lists all the predefined variables in the Tcl shells. argv is a list, so you can use the lindex command, which is described on page 59, to extract items from it: set arg1 [lindex $argv 0] The following script prints its arguments (foreach is described on page 73): Example 2-4 The EchoArgs script. # Tcl script to echo command line arguments puts "Program: $argv0" puts "Number of arguments: $argc" set i 0
Use a new private colormap. See page 540. Use the specified X display. UNIX only. The size and position of the window. See page 572. Specify the Tk application name. See page 562. Run X synchronously. UNIX only. Use the window specified by id for the main window. See page 580. Specify the visual for the main window. See page 540. Terminate options to wish.
Top
Table of Contents
Predefined Variables
Table 2-2. Variables defined by tclsh and wish.
argc argv argv0 embed_args env tcl_interactive tcl_library tcl_patchLevel tcl_platform tcl_prompt1 tcl_prompt2 tcl_version auto_path auto_index auto_noload auto_noexec geometry
The number of command-line arguments. A list of the command-line arguments. The name of the script being executed. If being used interactively, argv0 is the name of the shell program. The list of arguments in the <EMBED> tag. Tcl applets only. See page 298. An array of the environment variables. See page 117. True (one) if the tclsh is prompting for commands. The script library directory. Modified version number, e.g., 8.0b1. Array containing operating system information. See page 182. If defined, this is a command that outputs the prompt. If defined, this is a command that outputs the prompt if the current command is not yet complete. Version number. The search path for script library directories. See page 162. A map from command name to a Tcl command that defines it. If set, the library facility is disabled. If set, the auto execute facility is disabled. (wish only). The value of the -geometry argument.
Top
Table of Contents
Top
Table of Contents
Main tag that surrounds the whole document. Delimits head section of the HTML document. Defines the title of the page. Delimits the body section. Lets you specify page colors. HTML defines 6 heading levels: H1, H2, H3, H4, H5, H6. Start a new paragraph. One blank line. Bold text. Italic text. Used for hypertext links. Specify an image. Definition list. Term clause in a definition list. Definition clause in a definition list. An unordered list. A bulleted item within a list. Create a table. A table row. A cell within a table row. Defines a data entry form. A one-line entry field, checkbox, radio button, or submit button. A multiline text field.
Top
Table of Contents
The program computes a simple HTML page that has the current time. Each time a user visits the page they will see the current time on the server. The server that has the CGI program and the user viewing the page might be on different sides of the planet. The output of the program starts with a ContentType line that tells your Web browser what kind of data comes next. This is followed by a blank line and then the contents of the page.
The clock command is used twice: once to get the current time in seconds, and a second time to format the time into a nice looking string. The clock command is described in detail on page 173. Fortunately, there is no conflict between the markup syntax used by HTML and the Tcl syntax for embedded commands, so we can mix the two in the argument to the puts command. Double quotes are used to group the argument to puts so that the clock commands will be executed. When run, the output of the program will look like this: Example 3-2 Output of Example 3-1. Content-Type: text/html <TITLE>The Current Time</TITLE> The time is <B>Wed Oct 16 11:23:43 1996</B>
This example is a bit sloppy in its use of HTML, but it should display properly in most Web browsers. Example 3-3 includes all the required tags for a proper HTML document.
Top
Table of Contents
H2 Guests catch {source $datafile} foreach name [lsort [array names Guestbook]] { set item $Guestbook($name) set homepage [lindex $item 0] set markup [lindex $item 1] H3 [Link $name $homepage] puts $markup } } Cgi_End
</HEAD> <BODY $bodyparams> <H1>$title</H1>" } The Cgi_Header procedure takes as arguments the title for the page and some optional parameters for the HTML <Body> tag. The guestbook.cgi script specifies black text on a white background to avoid the standard gray background of most browsers. The procedure definition uses the syntax for an optional parameter, so you do not have to pass bodyparams to Cgi_Header. Default values for procedure parameters are described on page 81. The Cgi_Header procedure just contains a single puts command that generates the standard boilerplate that appears at the beginning of the output. Note that several lines are grouped together with double quotes. Double quotes are used so that the variable references mixed into the HTML are substituted properly. The output begins with the CGI content-type information, a blank line, and then the HTML. The HTML is divided into a head and a body part. The <TITLE> tag goes in the head section of an HTML document. Finally, browsers display the title in a different place than the rest of the page, so I always want to repeat the title as a level-one heading (i.e., H1) in the body of the page.
directly, it is returned, so you can include it in-line with other text you are printing: Example 3-5 The Link command formats a hypertext link. proc Link {text url} { return "<A HREF=\"$url\">$text</A>" } The output of the program would be as below if there were no data: Example 3-6 Initial output of guestbook.cgi. Content-Type: text/html <HTML> <HEAD> <TITLE>Brent's Guestbook</TITLE> </HEAD> <BODY BGCOLOR=white TEXT=black> <H1>Brent's Guestbook</H1> <P> No registered guests. <P> Be the first <A HREF="newguest.html">registered guest!</A> </BODY> </HTML> If the database file exists, then the real work begins. We first generate a link to the registration page, and a level-two header to separate that from the guest list: puts [Link Register newguest.html] H2 Guests The H2 procedure handles the detail of including the matching close tag: proc H2 {string} { puts "<H2>$string</H2>" }
kept in the same directory as the guestbook.cgi script, then you can compute its name: set dir [file dirname [info script]] set datafile [file join $dir guestbook.data] By using Tcl commands to represent the data, we can load the data with the source command. The catch command is used to protect the script from a bad data file, which will show up as an error from the source command. Catching errors is described in detail on page 79: catch {source $datafile} The Guestbook variable is the array defined in guestbook.data. Array variables are the topic of Chapter 8. Each element of the array is defined with a Tcl command that looks like this: set Guestbook(key) {url markup} The person's name is the array index, or key. The value of the array element is a Tcl list with two elements: their URL and some additional HTML markup that they can include in the guestbook. Tcl lists are the topic of Chapter 5. The following example shows what the command looks like with real data: set {Guestbook(Brent Welch)} { http://www.beedub.com/ {<img src=http://www.beedub.com/welch.gif>} } The spaces in the name result in additional braces to group the whole variable name and each list element. This syntax is explained on page 90. Do not worry about it now. We will see on page 42 that all the braces in the previous statement are generated automatically. The main point is that the person's name is the key, and the value is a list with two elements. The array names command returns all the indices, or keys, in the array, and the lsort command sorts these alphabetically. The foreach command loops over the sorted list, setting the loop variable x to each key in turn: foreach name [lsort [array names Guestbook]] { Given the key, we get the value like this: set item $Guestbook($name)
The two list elements are extracted with lindex, which is described on page 63. set homepage [lindex $item 0] set markup [lindex $item 1] We generate the HTML for the guestbook entry as a level-three header that contains a hypertext link to the guest's home page. We follow the link with any HTML markup text that the guest has supplied to embellish his or her entry. The H3 procedure is similar to the H2 procedure already shown, except it generates <H3> tags: H3 [Link $name $homepage] puts $markup
Sample Output
The last thing the script does is call Cgi_End to output the proper closing tags. Example 3-7 shows the output of the guestbook.cgi script: Example 3-7 Output of guestbook.cgi. Content-Type: text/html <HTML> <HEAD> <TITLE>Brent's Guestbook</TITLE> </HEAD> <BODY BGCOLOR=white TEXT=black> <H1>Brent's Guestbook</H1> <P> The following folks have registered in my guestbook. <P> <A HREF="newguest.html">Register</A> <H2>Guests</H2> <H3><A HREF="http://www.beedub.com/">Brent Welch</A></H3> <IMG SRC="http://www.beedub.com/welch.gif"> </BODY> </HTML>
Top
Practical Programming in Tcl & Tk, Third Edition By Brent B. Welch Table of Contents Chapter 3. The Guestbook CGI Application
<INPUT TYPE=submit NAME=submit VALUE=Register> A general type-in window is defined with the TEXTAREA tag. This creates a multiline, scrolling text field that is useful for specifying lots of information, such as a free-form comment. In our case we will let guests type in HTML that will appear with their guestbook entry. The text between the open and close TEXTAREA tags is inserted into the type-in window when the page is first displayed. <TEXTAREA NAME=markup ROWS=10 COLS=50>Hello.</TEXTAREA> A common parameter to the form tags is NAME= something . This name identifies the data that will come back from the form. The tags also have parameters that affect their display, such as the label on the submit button and the size of the text area. Those details are not important for our example. The complete form is shown in Example 3-8 : Example 3-8 The newguest.html form.
<!Doctype HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML> <HEAD> <TITLE>Register in my Guestbook</TITLE> <!-- Author: bwelch --> <META HTTP-Equiv=Editor Content="SunLabs WebTk 1.0beta 10/11/96"> </HEAD> <BODY> <FORM ACTION="newguest.cgi" METHOD="POST">
<H1>Register in my Guestbook</H1> <UL> <LI>Name <INPUT TYPE="text" NAME="name" SIZE="40"> <LI>URL <INPUT TYPE="text" NAME="url" SIZE="40"> <P> If you don't have a home page, you can use an email URL like "mailto:welch@acm.o <LI>Additional HTML to include after your link: <BR> <TEXTAREA NAME="html" COLS="60" ROWS="15"> </TEXTAREA> <LI><INPUT TYPE="submit" NAME="new" VALUE="Add me to your guestbook"> <LI><INPUT TYPE="submit" NAME="update" VALUE="Update my guestbook entry"> </UL>
#!/bin/sh # \ exec tclsh "$0" ${1+"$@"} # source cgilib.tcl from the same directory as newguest.cgi set dir [file dirname [info script]] source [file join $dir cgilib.tcl] set datafile [file join $dir guestbook.data] Cgi_Parse # Open the datafile in append mode if [catch {open $datafile a}out] { Cgi_Header "Guestbook Registration Error" \ {BGCOLOR=black TEXT=red} P puts "Cannot open the data file" P
puts $out;# the error message exit 0 } # Append a Tcl set command that defines the guest's entry puts $out "" puts $out [list set Guestbook([Cgi_Value name]) \ [list [Cgi_Value url] [Cgi_Value html]]] close $out # Return a page to the browser Cgi_Header "Guestbook Registration Confirmed" \ {BGCOLOR=white TEXT=black} puts " <DL> <DT>Name <DD>[Cgi_Value name] <DT>URL <DD>[Link [Cgi_Value url] [Cgi_Value url]] </DL> [Cgi_Value html] " Cgi_End The main idea of the newguest.cgi script is that it saves the data to a file as a Tcl command that defines an element of the Guestbook array. This lets the guestbook.cgi script simply load the data by using the Tcl source command. This trick of storing data as a Tcl script saves us from the chore of defining a new file format and writing code to parse it. Instead, we can rely on the well-tuned Tcl implementation to do the hard work for us efficiently. The script opens the datafile in append mode so that it can add a new record to the end. Opening files is described in detail on page 110. The script uses a catch command to guard against errors. If an error occurs, a page explaining the error is returned to the user. Working with files is one of the most common sources of errors (permission denied, disk full, file-not-found, and so on), so I always open the file inside a catch statement: if [catch {open $datafile a} out] { # an error occurred } else { # open was ok
} In this command, the variable out gets the result of the open command, which is either a file descriptor or an error message. This style of using catch is described in detail in Example 6-14 on page 77. The script writes the data as a Tcl set command. The list command is used to format the data properly: puts $out [list set Guestbook([Cgi_Value name]) \ [list [Cgi_Value url] [Cgi_Value html]]] There are two lists. First the url and html values are formatted into one list. This list will be the value of the array element. Then, the whole Tcl command is formed as a list. In simplified form, the command is generated from this: list set variable value Using the list command ensures that the result will always be a valid Tcl command that sets the variable to the given value. The list command is described in more detail on page 61.
Top
Table of Contents
Top
Table of Contents
Next Steps
There are a number of details that can be added to this example. A user may want to update their entry, for example. They could do that now, but they would have to retype everything. They might also like a chance to check the results of their registration and make changes before committing them. This requires another page that displays their guest entry as it would appear on a page, and also has the fields that let them update the data. The details of how a CGI script is hooked up with a Web server vary from server to server. You should ask your local Webmaster for help if you want to try this out on your local web site. The Tcl Web Server comes with this guestbook example already set up, plus it has a number of other very interesting ways to generate pages. My own taste in web page generation has shifted from CGI to a template-based approach supported by the Tcl Web Server. This is the topic of Chapter 18. The next few chapters describe basic Tcl commands and data structures. We return to this example in Chapter 11 on regular expressions.
Top
Table of Contents
Top
Table of Contents
Returns the number of bytes used to store a string, which may be different from the character length returned by string length because of UTF-8 encoding. See page 210 of Chapter 15 about Unicode and UTF-8. Compares strings lexicographically. Use -nocase for case insensitve comparison. Use -length to limit the comparison to the first len characters. Returns 0 if equal, -1 if str1 sorts before str2, else 1.
Compares strings and returns 1 if they are the same. Use -nocase for case insensitve comparison. Returns the index in str2 of the first occurrence of str1, or -1 if str1 is not found. An index counts from zero. Use
string index string Returns the character at the specified index. index end for the last character. string is class ?strict? ?failindex varname? string
Returns 1 if string belongs to class. If -strict, then empty strings never match, otherwise they always match. If -failindex is specified, then varname is assigned the index of the character in string that prevented it from being a member of class. See Table 4-3 on page 50 for character class names. Returns the index in str2 of the last occurrence of str1, or -1 if str1 is not found. Returns the number of characters in string. Returns a new string created by mapping characters in string according to the input, output list in charMap. See page 51. Returns 1 if str matches the pattern, else 0. Glob-style matching is used. See page 48. Returns the range of characters in str from i to j. Returns str repeated count times. Returns a new string created by replacing characters first through last with newstr, or nothing. Returns string in lower case. first and last determine the range of string on which to operate. Capitalizes string by replacing its first character with the Unicode title case, or upper case, and the rest with lower case. first and last determine the range of string on which to operate. Returns string in upper case. first and last determine the range of string on which to operate. Trims the characters in chars from both ends of string. chars defaults to whitespace. Trims the characters in chars from the beginning of string. chars defaults to whitespace. Trims the characters in chars from the end of string. chars defaults to whitespace.
string last str1 str2 string length string string map ?nocase? charMap string string match pattern str string range str i j string repeat str count string replace str first last?newstr? string tolower string?first? ? last? string totitle string?first? ? last? string toupper string?first? ? last? string trim string?chars? string trimleft string?chars? string trimright string?chars?
Returns the index in str of the character after the word containing the character at index ix. Returns the index in str of the first character in the word containing the character at index ix.
These are the string operations I use most: The equal operation, which is shown in Example 4-2 on page 48. String match. This pattern matching operation is described on page 48. The tolower, totitle, and toupper operations convert case. The trim, trimright, and trimleft operations are handy for cleaning up strings. These new operations were added in Tcl 8.1 (actually, they first appeared in the 8.1.1 patch release): The equal operation, which is simpler than using string compare. The is operation that test for kinds of strings. String classes are listed in Table 4-3 on page 50. The map operation that translates characters (e.g., like the Unix tr command.) The repeat and replace operations. The totitle operation, which is handy for capitalizing words.
String Indices
Several of the string operations involve string indices that are positions within a string. Tcl counts characters in strings starting with zero. The special index end is used to specify the last character in a string: string range abcd 2 end => cd Tcl 8.1 added syntax for specifying an index relative to the end. Specify end-N to get the Nth caracter before the end. For example, the following command returns a new string that drops the first and last characters from the original: string range $string 1 end-1 There are several operations that pick apart strings: first, last, wordstart, wordend, index, and range. If you find yourself using combinations of these operations to pick apart data, it will be faster if you can do it with the regular expression pattern matcher described in Chapter 11.
Ironically, despite the quotes, the expression evaluator first converts items to numbers if possible, and then converts them back if it detects a case of string comparison. The conversion back is always done as a decimal number. This can lead to unexpected conversions between strings that look like hexadecimal or octal numbers. The following boolean expression is true! if {"0xa" == "10"} {puts stdout ack! } => ack! A safe way to compare strings is to use the string compare and equal operations. These operations work faster because the unnecessary conversions are eliminated. Like the C library strcmp function, string compare returns 0 if the strings are equal, minus 1 if the first string is lexicographically less than the second, or 1 if the first string is greater than the second: Example 4-1 Comparing strings with string compare. if {[string compare $s1 $s2] == 0} { # strings are equal } The string equal command added in Tcl 8.1 makes this simpler: Example 4-2 Comparing strings with string equal. if {[string equal $s1 $s2]} { # strings are equal }
String Matching
The string match command implements glob-style pattern matching that is modeled after the file name pattern matching done by various UNIX shells. The heritage of the word "glob" is rooted in UNIX, and Tcl preserves this historical oddity in the glob command that does pattern matching on file names. The glob command is described on page 115. Table 4-2 shows the three constructs used in string match patterns:
Match any number of any characters. Match exactly one character. Match any character in chars.
Any other characters in a pattern are taken as literals that must match the input exactly. The following example matches all strings that begin with a: string match a* alpha => 1 To match all two-letter strings: string match ?? XY => 1 To match all strings that begin with either a or b: string match {[ab]*}cello => 0 Be careful! Square brackets are also special to the Tcl interpreter, so you will need to wrap the pattern up in curly braces to prevent it from being interpreted as a nested command. Another approach is to put the pattern into a variable: set pat {[ab]*x} string match $pat box => 1
You can specify a range of characters with the syntax [x-y]. For example, [a-z] represents the set of all lower-case letters, and [0-9] represents all the digits. You can include more than one range in a set. Any letter, digit, or the underscore is matched with: string match {[a-zA-Z0-9_]}$char The set matches only a single character. To match more complicated patterns, like one or more characters from a set, then you need to use regular expression matching, which is described on page 148. If you need to include a literal *, ?, or bracket in your pattern, preface it with a backslash: string match {*\?}what? => 1 In this case the pattern is quoted with curly braces because the Tcl interpreter is also doing backslash substitutions. Without the braces, you would have to use two backslashes. They are replaced with a single backslash by Tcl before string match is called. string match *\\? what?
Character Classes
The string is command tests a string to see whether it belongs to a particular class. This is useful for input validation. For example, to make sure something is a number, you do: if {![string is integer $input]} { error "Invalid input. Please enter a number." } Classes are defined in terms of the Unicode character set, which means they are more general than specifying character sets with ranges over the ASCII encoding. For example, alpha includes many characters outside the range of [A-Za-z] because of different characters in other alphabets. The classes are listed in Table 4-3.
alnum alpha ascii boolean control digit double false graph integer lower print punct space true upper wordchar xdigit
Any alphabet or digit character. Any alphabet character. Any character with a 7-bit character code (i.e., less than 128.)
0, 1, true, false (in any case).
Character code less than 32, and not NULL. Any digit character. A valid floating point number.
0
Any printing characters, not including space characters. A valid integer. A string in all lower case. A synonym for alnum. Any punctuation character. Space, tab, newline, carriage return, vertical tab, backspace.
1
A string all in upper case. Alphabet, digit, and the underscore. Valid hexadecimal digits.
Mapping Strings
The string map command translates a string based on a character map. The map is in the form of a input, output list. Whereever a string contains an input sequence, that is replaced with the corresponding output. For example: string map "food" {f p d l} => pool The inputs and outputs can be more than one character and do not have to be the same length: string map "food" {f p d ll oo u} => pull Example 4-3 is more practical. It uses string map to replace fancy quotes and hyphens produced by Microsoft Word into ASCII equivalents. It uses the open, read, and close file operations that are described in Chapter 9, and the fconfigure command described on page 223 to ensure that the file
format is UNIX friendly. Example 4-3 Mapping Microsoft World special characters to ASCII. proc Dos2Unix {filename} { set input [open $filename] set output [open $filename.new] fconfigure $output -translation lf puts $output [string map { \223 " \224 " \222 ' \226 }[read $input]] close $input close $output }
Top
Table of Contents
The append command provides an efficient way to add items to the end of a string. It modifies a variable directly, so it can exploit the memory allocation scheme used internally by Tcl. Using the append command like this: append x " some new stuff" is always faster than this: set x "$x some new stuff" The lappend command described on page 61 has similar performance benefits when working with Tcl lists.
Top
Table of Contents
d u i o x or X c s f e or E g or G
Signed integer. Unsigned integer. Signed integer. The argument may be in hex (0x) or octal (0) format. Unsigned octal. Unsigned hexadecimal. 'x' gives lowercase results. Map from an integer to the ASCII character it represents. A string. Floating point number in the format a.b. Floating point number in scientific notation, a.bE+-c. Floating point number in either %f or %e format, whichever is shorter.
A position specifier is i$, which means take the value from argument i as opposed to the normally corresponding argument. The position counts from 1. If a position is specified for one format keyword, the position must be used for all of them. If you group the format specification with double quotes, you need to quote the $ with a backslash: set lang 2 format "%${lang}\$s" one un uno => un The position specifier is useful for picking a string from a set, such as this simple language-specific example. The message catalog facility described in Chapter 15 is a much more sophisticated way to solve this problem. The position is also useful if the same value is repeated in the formatted string. The flags in a format are used to specify padding and justification. In the following examples, the # causes a leading 0x to be printed in the hexadecimal value. The zero in 08 causes the field to be padded with zeros. Table 4-5 summarizes the format flag characters. format "%#x" 20 => 0x14 format "%#08x" 10 => 0x0000000a
+ space 0 #
Left justify the field. Always include a sign, either + or -. Precede a number with a space, unless the number has a leading sign. Useful for packing numbers close together. Pad with zeros. Leading 0 for octal. Leading 0x for hex. Always include a decimal point in floating point. Do not remove trailing zeros (%g).
After the flags you can specify a minimum field width value. The value is padded to this width with spaces, or with zeros if the 0 flag is used: format "%-20s %3d" Label 2 => Label 2 You can compute a field width and pass it to format as one of the arguments by using * as the field width specifier. In this case the next argument is used as the field width instead of the value, and the argument after that is the value that gets formatted. set maxl 8 format "%-*s = %s" $maxl Key Value => Key = Value The precision comes next, and it is specified with a period and a number. For %f and %e it indicates how many digits come after the decimal point. For %g it indicates the total number of significant digits used. For %d and %x it indicates how many digits will be printed, padding with zeros if necessary. format "%6.2f %6.2d" 1 1 => 1.00 01 The storage length part comes last but it is rarely useful because Tcl maintains all floating point values in double-precision, and all integers as long words.
Top
Table of Contents
Top
Table of Contents
Format Templates
The template consists of type keys and counts. The types are summarized in Table 4-6. In the table, count is the optional count following the type letter.
a A b B h H c s S i I f d x
A character string of length count. Padded with nulls in binary format. A character string of length count. Padded with spaces in binary format. Trailing nulls and blanks are discarded in binary scan. A binary string of length count. Low-to-high order. A binary string of length count. High-to-low order. A hexadecimal string of length count. Low-to-high order. A hexadecimal string of length count. High-to-low order. (More commonly used than h.) An 8-bit character code. The count is for repetition. A 16-bit integer in little-endian byte order. The count is for repetition. A 16-bit integer in big-endian byte order. The count is for repetition. A 32-bit integer in little-endian byte order. The count is for repetition. A 32-bit integer in big-endian byte order. The count is for repetition. Single-precision floating point value in native format. count is for repetition. Double-precision floating point value in native format. count is for repetition. Pack count null bytes with binary format. Skip count bytes with binary scan.
X @
Backup count bytes. Skip to absolute position specified by count. If count is *, skip to the end.
The count is interpreted differently depending on the type. For types like integer (i) and double (d), the count is a repetition count (e.g., i3 means three integers). For strings, the count is a length (e.g., a3 means a three-character string). If no count is specified, it defaults to 1. If count is *, then binary scan uses all the remaining bytes in the value. Several type keys can be specified in a template. Each key-count combination moves an imaginary cursor through the binary data. There are special type keys to move the cursor. The x key generates null bytes in binary format, and it skips over bytes in binary scan. The @ key uses its count as an absolute byte offset to which to set the cursor. As a special case, @* skips to the end of the data. The X key backs up count bytes. Numeric types have a particular byte order that determines how their value is laid out in memory. The type keys are lowercase for little-endian byte order (e.g., Intel) and uppercase for big-endian byte order (e.g., SPARC and Motorola). Different integer sizes are 16-bit (s or S), 32-bit (i or I), and possibly 64-bit (l or L) on those machines that support 64-bit integers. Note that the official byte order for data transmitted over a network is big-endian. Floating point values are always machine-specific, so it only makes sense to format and scan these values on the same machine. There are three string types: character (a or A), binary (b or B), and hexadecimal (h or H). With these types the count is the length of the string. The a type pads its value to the specified length with null bytes in binary format and the A type pads its value with spaces. If the value is too long, it is truncated. In binary scan, the A type strips trailing blanks and nulls.
A binary string consists of zeros and ones. The b type specifies bits from low-to-high order, and the B type specifies bits from high-to-low order. A hexadecimal string specifies 4 bits (i.e., nybbles) with each character. The h type specifies nybbles from low-to-high order, and the H type specifies nybbles from high-to-low order. The B and H formats match the way you normally write out numbers.
Examples
When you experiment with binary format and binary scan, remember that Tcl treats things as strings by default. A "6", for example, is the character 6 with character code 54 or 0x36. The c type returns these character codes: set input 6 binary scan $input "c" 6val set 6val => 54 You can scan several character codes at a time: binary scan abc "c3" list => 1 set list => 97 98 99 The previous example uses a single type key, so binary scan sets one corresponding Tcl variable. If you want each character code in a separate variable, use separate type keys: binary scan abc "ccc" x y z => 3 set z => 99 Use the H format to get hexadecimal values: binary scan 6 "H2" 6val set 6val => 36 Use the a and A formats to extract fixed width fields. Here the * count is used to get all the rest of the string. Note that A trims trailing spaces: binary scan "hello world " a3x2A* first second puts "\"$first\" \"$second\""
=> "hel" " world" Use the @ key to seek to a particular offset in a value. The following command gets the second doubleprecision number from a vector. Assume the vector is read from a binary data file: binary scan $vector "@8d" double With binary format, the a and A types create fixed width fields. A pads its field with spaces, if necessary. The value is truncated if the string is too long: binary format "A9A3" hello world => hello wor An array of floating point values can be created with this command: binary format "f*" 1.2 3.45 7.43 -45.67 1.03e4 Remember that floating point values are always in native format, so you have to read them on the same type of machine that they were created. With integer data you specify either big-endian or littleendian formats. The tcl_platform variable described on page 182 can tell you the byte order of the current platform.
Top
Table of Contents
Related Chapters
To learn more about manipulating data in Tcl, read about lists in Chapter 5 and arrays in Chapter 8. For more about pattern matching, read about regular expressions in Chapter 11. For more about file I/O, see Chapter 9. For information on Unicode and other Internationalization issues, see Chapter 15.
Top
Table of Contents
Top
Table of Contents
Tcl Lists
A Tcl list is a sequence of values. When you write out a list, it has the same syntax as a Tcl command. A list has its elements separated by white space.Braces or quotes can be used to group words with white space into a single list element. Because of the relationship between lists and commands, the list-related commands described in this chapter are used often when constructing Tcl commands. Big lists were often slow before Tcl 8.0.
Unlike list data structures in other languages, Tcl lists are just strings with a special interpretation. The string representation must be parsed on each list access, so be careful when you use large lists. A list with a few elements will not slow down your code much. A list with hundreds or thousands of elements can be very slow. If you find yourself maintaining large lists that must be frequently accessed, consider changing your code to use arrays instead. The performance of lists was improved by the Tcl compiler added in Tcl 8.0. The compiler stores lists in an internal format that requires constant time to access. Accessing the first element costs the same as accessing any other element in the list. Before Tcl 8.0, the cost of accessing an element was proportional to the number of elements before it in the list. The internal format also records the number of list elements, so getting the length of a list is cheap. Before Tcl 8.0, computing the length required reading the whole list. Table 5-1 briefly describes the Tcl commands related to lists.
list arg1 arg2 ... lindex list i llength list lrange list i j lappend listVar arg arg ... linsert list index arg arg ... lreplace list i j arg arg ... lsearch ?mode? list value lsort ?switches? list
Creates a list out of all its arguments. Returns the ith element from list. Returns the number of elements in list. Returns the ith through jth elements from list. Appends elements to the value of listVar. Inserts elements into list before the element at position index. Returns a new list. Replaces elements i through j of list with the args. Returns a new list. Returns the index of the element in list that matches the value according to the mode, which is -exact, -glob, or -regexp. -glob is the default. Returns -1 if not found. Sorts elements of the list according to the switches: -ascii, -integer, real, -dictionary, -increasing, -decreasing, -index ix , -command command . Returns a new list.
concat list list ... join list joinString split string splitChars
Joins multiple lists together into one list. Merges the elements of a list together by separating them with joinString. Splits a string up into list elements, using the characters in splitChars as boundaries between list elements.
Top
Table of Contents
Constructing Lists
Constructing a list can be tricky because you must maintain proper list syntax. In simple cases, you can do this by hand. In more complex cases, however, you should use Tcl commands that take care of quoting so that the syntax comes out right.
Compare the use of list with doing the quoting by hand in Example 5-1. The assignment of l2
requires carefully constructing the first list element by using quoted braces. The braces must be turned off so that $x can be substituted, but we need to group the result so that it remains a single list element. We also have to know in advance that $x contains a space, so quoting is required. We are taking a risk by not quoting $y because we know it doesn't contain spaces. If its value changes in the future, the structure of the list can change and even become invalid. In contrast, the list command takes care of all these details automatically. When I first experimented with Tcl lists, I became confused by the treatment of curly braces. In the assignment to x, for example, the curly braces disappear. However, they come back again when $x is put into a bigger list. Also, the double quotes around a b get changed into curly braces. What's going on? Remember that there are two steps. In the first step, the Tcl parser groups arguments. In the grouping process, the braces and quotes are syntax that define groups. These syntax characters get stripped off. The braces and quotes are not part of the value. In the second step, the list command creates a valid Tcl list. This may require quoting to get the list elements into the right groups. The list command uses curly braces to group values back into list elements.
The lappend command is unique among the list-related commands because its first argument is the name of a list-valued variable, while all the other commands take list values as arguments. You can call lappend with the name of an undefined variable and the variable will be created. The lappend command is implemented efficiently to take advantage of the way that Tcl stores lists internally. It is always more efficient to use lappend than to try and append elements by hand.
set x {4 5 6} set y {2 3} set z 1 concat $z $y $x => 1 2 3 4 5 6 Double quotes behave much like the concat command. In simple cases, double quotes behave exactly like concat. However, the concat command trims extra white space from the end of its arguments before joining them together with a single separating space character. Example 5-4 compares the use of list, concat, and double quotes: Example 5-4 Double quotes compared to the concat and list commands. set x {1 2} => 1 2 set y "$x 3" => 1 2 3 set y [concat $x 3] => 1 2 3 set s { 2 } => 2 set y "1 $s 3" => 1 2 3 set y [concat 1 $s 3] => 1 2 3 set z [list $x $s 3] => {1 2} { 2 } 3 The distinction between list and concat becomes important when Tcl commands are built dynamically. The basic rule is that list and lappend preserve list structure, while concat (or double quotes) eliminates one level of list structure. The distinction can be subtle because there are examples where list and concat return the same results. Unfortunately, this can lead to data-dependent bugs. Throughout the examples of this book, you will see the list command used to safely construct lists. This issue is discussed more in Chapter 10.
Top
Table of Contents
Top
Table of Contents
you effectively delete elements from a list. Note: linsert and lreplace do not modify an existing list. Instead, they return a new list value. In the following example, the lreplace command does not change the value of x: Example 5-5 Modifying lists with linsert and lreplace. linsert {1 2}0 new stuff => new stuff 1 2 set x [list a {b c}e d] => a {b c}e d lreplace $x 1 2 B C => a B C d lreplace $x 0 0 => {b c}e d
Top
Table of Contents
lsearch {here is a list}l* => 3 Example 5-6 uses lreplace and lsearch to delete a list element by value. The value is found with lsearch . The value is removed with an lreplace that does not specify any replacement list elements: Example 5-6 Deleting a list element by value. proc ldelete {list value } { set ix [lsearch -exact $list $value] if {$ix >= 0} { return [lreplace $list $ix $ix] } else { return $list } }
Top
Table of Contents
=> {Miles Davis} {John Ousterhout} {Brent B. Welch} The NameCompare procedure extracts the last element from each of its arguments and compares those. If they are equal, then it just compares the whole of each argument. Tcl 8.0 added a -index option to lsort that can be used to sort lists on an index. Instead of using NameCompare, you could do this: lsort -index end $list
Top
Table of Contents
Even if your data has space-separated words, you should be careful when using list operators on arbitrary input data. Otherwise, stray double quotes or curly braces in the input can result in invalid list structure and errors in your script. Your code will work with simple test cases, but when invalid list syntax appears in the input, your script will raise an error. The next example shows what happens when input is not a valid list. The syntax error, an unmatched quote, occurs in the middle of the list. However, you cannot access any of the list because the lindex command tries to convert the value to a list before returning any part of it. Example 5-8 Use split to turn input data into Tcl lists. set line {this is "not a tcl list} lindex $line 1 => unmatched open quote in list lindex [split $line] 2
=> "not The default separator character for split is white space, which contains spaces, tabs, and newlines. If there are multiple separator characters in a row, these result in empty list elements; the separators are not collapsed. The following command splits on commas, periods, spaces, and tabs. The backslashspace sequence is used to include a space in the set of characters. You could also group the argument to split with double quotes: set line "\tHello, world." split $line \,.\t => {}Hello {}world {} A trick that splits each character into a list element is to specify an empty string as the split character. This lets you get at individual characters with list operations: split abc {} => a b c However, if you write scripts that process data one character at a time, they may run slowly. Read Chapter 11 about regular expressions for hints on really efficient string processing.
Top
Table of Contents
Top
Table of Contents
Related Chapters
Arrays are the other main data structure in Tcl. They are described in Chapter 8. List operations are used when generating Tcl code dynamically. Chapter 10 describes these techniques when using the eval command. The foreach command loops over the values in a list. It is described on page 73 in Chapter 6.
Top
Table of Contents
Top
Table of Contents
If Then Else
The if command is the basic conditional command. If an expression is true, then execute one command body; otherwise, execute another command body. The second command body (the else clause) is optional. The syntax of the command is: if expression ?then? body1 ?else? ?body2? The then and else keywords are optional. In practice, I omit then but use else as illustrated in the next example. I always use braces around the command bodies, even in the simplest cases: Example 6-1 A conditional if then else command. if {$x == 0} { puts stderr "Divide by zero!" } else { set slope [expr $y/$x] } Curly brace positioning is important.
The style of this example takes advantage of the way the Tcl interpreter parses commands. Recall that newlines are command terminators, except when the interpreter is in the middle of a group defined by braces or double quotes. The stylized placement of the opening curly brace at the end of the first and third lines exploits this property to extend the if command over multiple lines. The first argument to if is a boolean expression. As a matter of style this expression is grouped with
curly braces. The expression evaluator performs variable and command substitution on the expression. Using curly braces ensures that these substitutions are performed at the proper time. It is possible to be lax in this regard, with constructs such as: if $x break continue This is a sloppy, albeit legitimate, if command that will either break out of a loop or continue with the next iteration depending on the value of variable x. This style is fragile and error prone. Instead, always use braces around the command bodies to avoid trouble later when you modify the command. The following is much better (use then if it suits your taste): if {$x} { break } else { continue } When you are testing the result of a command, you can get away without using curly braces around the command, like this: if [command] body1 However, it turns out that you can execute the if statement more efficiently if you always group the expression with braces, like this: if {[command]}body1 You can create chained conditionals by using the elseif keyword. Again, note the careful placement of curly braces that create a single if command: Example 6-2 Chained conditional with elseif. if {$key < 0} { incr range 1 } elseif {$key == 0} { return $range } else { incr range -1 } Any number of conditionals can be chained in this manner. However, the switch command provides a
Top
Table of Contents
Switch
The switch command is used to branch to one of many command bodies depending on the value of an expression. The choice can be made on the basis of pattern matching as well as simple comparisons. Pattern matching is discussed in more detail in Chapter 4 and Chapter 11. The general form of the command is: switch flags value pat1 body1 pat2 body2 ... Any number of pattern-body pairs can be specified. If multiple patterns match, only the body of the first matching pattern is evaluated. You can also group all the pattern-body pairs into one argument: switch flags value {pat1 body1 pat2 body2 ... } The first form allows substitutions on the patterns but will require backslashes to continue the command onto multiple lines. This is shown in Example 6-4 on page 72. The second form groups all the patterns and bodies into one argument. This makes it easy to group the whole command without worrying about newlines, but it suppresses any substitutions on the patterns. This is shown in Example 6-3. In either case, you should always group the command bodies with curly braces so that substitution occurs only on the body with the pattern that matches the value. There are four possible flags that determine how value is matched.
-exact -glob -regexp --
Matches the value exactly to one of the patterns. This is the default. Uses glob-style pattern matching. See page 48. Uses regular expression pattern matching. See page 134. No flag (or end of flags). Necessary when value can begin with -.
The switch command raises an error if any other flag is specified or if the value begins with -. In practice I always use the -- flag before value so that I don't have to worry about that problem.
If the pattern associated with the last body is default, then this command body is executed if no other patterns match. The default keyword works only on the last pattern-body pair. If you use the default pattern on an earlier body, it will be treated as a pattern to match the literal string default: Example 6-3 Using switch for an exact match. switch -exact -- $value { foo { doFoo; incr count(foo) } bar { doBar; return $count(foo)} default { incr count(other) } } If you have variable references or backslash sequences in the patterns, then you cannot use braces around all the pattern-body pairs. You must use backslashes to escape the newlines in the command: Example 6-4 Using switch with substitutions in the patterns. switch -regexp -- $value \ ^$key { body1 }\ \t### { body2 }\ {[0-9]*} { body3 } In this example, the first and second patterns have substitutions performed to replace $key with its value and \t with a tab character. The third pattern is quoted with curly braces to prevent command substitution; square brackets are part of the regular expression syntax, too. (See page Chapter 11.) If the body associated with a pattern is just a dash, -, then the switch command "falls through" to the body associated with the next pattern. You can tie together any number of patterns in this manner. Example 6-5 A switch with "fall through" cases. switch -glob -- $value { X* Y* { takeXorYaction $value } }
Example 6-6 Comments in switch commands. switch -- $value { # this comment confuses switch pattern { # this comment is ok } }
Top
Table of Contents
While
The while command takes two arguments, a test and a command body: while booleanExpr body The while command repeatedly tests the boolean expression and then executes the body if the expression is true (nonzero). Because the test expression is evaluated again before each iteration of the loop, it is crucial to protect the expression from any substitutions before the while command is invoked. The following is an infinite loop (see also Example 1-13 on page 12): set i 0 ; while $i<10 {incr i} The following behaves as expected: set i 0 ; while {$i<10} {incr i} It is also possible to put nested commands in the boolean expression. The following example uses gets to read standard input. The gets command returns the number of characters read, returning -1 upon end of file. Each time through the loop, the variable line contains the next line in the file: Example 6-7 A while loop to read standard input. set numLines 0 ; set numChars 0 while {[gets stdin line] >= 0} { incr numLines incr numChars [string length $line] }
Top
Table of Contents
Foreach
The foreach command loops over a command body assigning one or more loop variables to each of the values in one or more lists. Multiple loop variables were introduced in Tcl 7.5. The syntax for the simple case of a single variable and a single list is: foreach loopVar valueList commandBody The first argument is the name of a variable, and the command body is executed once for each element in the list with the loop variable taking on successive values in the list. The list can be entered explicitly, as in the next example: Example 6-8 Looping with foreach. set i 1 foreach value {1 3 5 7 11 13 17 19 23} { set i [expr $i*$value] } set i => 111546435 It is also common to use a list-valued variable or command result instead of a static list value. The next example loops through command-line arguments. The variable argv is set by the Tcl interpreter to be a list of the command-line arguments given when the interpreter was started: Example 6-9 Parsing command-line arguments. # # # # argv is set by the Tcl shells possible flags are: -max integer -force
# -verbose set state flag set force 0 set verbose 0 set max 10 foreach arg $argv { switch -- $state { flag { switch -glob -- $arg { -f* {set force 1} -v* {set verbose 1} -max {set state max} default {error "unknown flag $arg"} } } max { set max $arg set state flag } } } The loop uses the state variable to keep track of what is expected next, which in this example is either a flag or the integer value for -max. The -- flag to switch is required in this example because the switch command complains about a bad flag if the pattern begins with a - character. The -glob option lets the user abbreviate the -force and -verbose options. If the list of values is to contain variable values or command results, then the list command should be used to form the list. Avoid double quotes because if any values or command results contain spaces or braces, the list structure will be reparsed, which can lead to errors or unexpected results. Example 6-10 Using list with foreach. foreach x [list $a $b [foo]] { puts stdout "x = $x" } The loop variable x will take on the value of a, the value of b, and the result of the foo command, regardless of any special characters or whitespace in those values.
the empty string as their value. Example 6-11 Multiple loop variables with foreach. foreach {key value} {orange 55 blue 72 red 24 green} { puts "$key: $value" } orange: 55 blue: 72 red: 24 green: If you have a command that returns a short list of values, then you can abuse the foreach command to assign the results of the commands to several variables all at once. For example, suppose the command MinMax returns two values as a list: the minimum and maximum values. Here is one way to get the values: set result [MinMax $list] set min [lindex $result 0] set max [lindex $result 1] The foreach command lets us do this much more compactly: foreach {min max}[MinMax $list] {break} The break in the body of the foreach loop guards against the case where the command returns more values than we expected. This trick is encapsulated into the lassign procedure in Example 10-4 on page 131.
black : 24
Top
Table of Contents
For
The for command is similar to the C for statement. It takes four arguments: for initial test final body The first argument is a command to initialize the loop. The second argument is a boolean expression that determines whether the loop body will execute. The third argument is a command to execute after the loop body: Example 6-13 A for loop. for {set i 0} {$i < 10} {incr i 3} { lappend aList $i } set aList => 0 3 6 9 You could use for to iterate over a list, but you should really use foreach instead. Code like the following is slow and cluttered: for {set i 0} {$i < [llength $list]} {incr i} { set value [lindex $list $i] } This is the same as: foreach value $list { }
Top
Table of Contents
Break
and Continue
You can control loop execution with the break and continue commands. The break command causes immediate exit from a loop, while the continue command causes the loop to continue with the next iteration. There is no goto command in Tcl.
Top
Table of Contents
Catch
Until now we have ignored the possibility of errors. In practice, however, a command will raise an error if it is called with the wrong number of arguments, or if it detects some error condition particular to its implementation. An uncaught error aborts execution of a script.[*] The catch command is used to trap such errors. It takes two arguments:
[*] More
precisely, the Tcl script unwinds and the current Tcl_Eval procedure in the C runtime library returns TCL_ERROR . There are three cases. In interactive use, the Tcl shell prints the error message. In Tk, errors that arise during event handling trigger a call to bgerror, a Tcl procedure you can implement in your application. In your own C code, you should check the result of Tcl_Eval and take appropriate action in the case of an error.
catch command ?resultVar? The first argument to catch is a command body. The second argument is the name of a variable that will contain the result of the command, or an error message if the command raises an error. catch returns zero if there was no error caught, or a nonzero error code if it did catch an error. You should use curly braces to group the command instead of double quotes because catch invokes the full Tcl interpreter on the command. If double quotes are used, an extra round of substitutions occurs before catch is even called. The simplest use of catch looks like the following: catch {command } A more careful catch phrase saves the result and prints an error message: Example 6-14 A standard catch phrase. if {[catch { command arg1 arg2 ... }result]} { puts stderr $result } else {
# command was ok, result contains the return value } A more general catch phrase is shown in the next example. Multiple commands are grouped into a command body. The errorInfo variable is set by the Tcl interpreter after an error to reflect the stack trace from the point of the error: Example 6-15 A longer catch phrase. if {[catch { command1 command2 command3 } result]} { global errorInfo puts stderr $result puts stderr "*** Tcl TRACE ***" puts stderr $errorInfo } else { # command body ok, result of last command is in result } These examples have not grouped the call to catch with curly braces. This is acceptable because catch always returns an integer, so the if command will parse correctly. However, if we had used while instead of if , then curly braces would be necessary to ensure that the catch phrase was evaluated repeatedly.
Top
Table of Contents
Error
The error command raises an error condition that terminates a script unless it is trapped with the catch command. The command takes up to three arguments: error message ?info? ?code? The message becomes the error message stored in the result variable of the catch command. If the info argument is provided, then the Tcl interpreter uses this to initialize the errorInfo global variable. That variable is used to collect a stack trace from the point of the error. If the info argument is not provided, then the error command itself is used to initialize the errorInfo trace. Example 6-17 Raising an error. proc foo {} { error bogus } foo => bogus set errorInfo => bogus while executing "error bogus" (procedure "foo" line 2) invoked from within "foo" In the previous example, the error command itself appears in the trace. One common use of the info argument is to preserve the errorInfo that is available after a catch. In the next example, the information from the original error is preserved:
Example 6-18 Preserving errorInfo when calling error. if {[catch {foo}result]} { global errorInfo set savedInfo $errorInfo # Attempt to handle the error here, but cannot... error $result $savedInfo } The code argument specifies a concise, machine-readable description of the error. It is stored into the global errorCode variable. It defaults to NONE. Many of the file system commands return an errorCode that has three elements: POSIX, the error name (e.g., ENOENT), and the associated error message: POSIX ENOENT {No such file or directory} In addition, your application can define error codes of its own. Catch phrases can examine the code in the global errorCode variable and decide how to respond to the error.
Top
Table of Contents
Return
The return command is used to return from a procedure. It is needed if return is to occur before the end of the procedure body, or if a constant value needs to be returned. As a matter of style, I also use return at the end of a procedure, even though a procedure returns the value of the last command executed in the body. Exceptional return conditions can be specified with some optional arguments to return. The complete syntax is: return ?-code c? ?-errorinfo i? ?-errorcode ec? string The -code option value is one of ok, error, return, break, continue, or an integer. ok is the default if -code is not specified. The -code error option makes return behave much like the error command. The -errorcode option sets the global errorCode variable, and the -errorinfo option initializes the errorInfo global variable. When you use return -code error, there is no error command in the stack trace. Compare Example 6-17 with Example 6-19: Example 6-19 Raising an error with return. proc bar {} { return -code error bogus } catch {bar}result => 1 set result => bogus set errorInfo => bogus while executing "bar"
The return, break, and continue code options take effect in the caller of the procedure doing the exceptional return. If -code return is specified, then the calling procedure returns. If -code break is specified, then the calling procedure breaks out of a loop, and if -code continue is specified, then the calling procedure continues to the next iteration of the loop. These -code options to return enable the construction of new control structures entirely in Tcl. The following example implements the break command with a Tcl procedure: proc break {} { return -code break }
Top
Table of Contents
Top
Table of Contents
A procedure can take a variable number of arguments by specifying the args keyword as the last parameter. When the procedure is called, the args parameter is a list that contains all the remaining values: Example 7-2 Variable number of arguments. proc ArgTest {a {b foo}args} { foreach param {a b args} { puts stdout "\t$param = [set $param]" } } set x one set y {two things} set z \[special\$ ArgTest $x => a = one b = foo args = ArgTest $y $z => a = two things b = [special$ args = ArgTest $x $y $z => a = one b = two things args = {[special$} ArgTest $z $y $z $x => a = [special$ b = two things args = {[special$}one The effect of the list structure in args is illustrated by the treatment of variable z in Example 7-2. The value of z has special characters in it. When $z is passed as the value of parameter b, its value comes through to the procedure unchanged. When $z is part of the optional parameters, quoting is automatically added to create a valid Tcl list as the value of args. Example 10-3 on page 127 illustrates a technique that uses eval to undo the effect of the added list structure.
Top
Table of Contents
Top
Table of Contents
Scope
By default there is a single, global scope for procedure names. This means that you can use a procedure anywhere in your script. Variables defined outside any procedure are global variables. However, as described below, global variables are not automatically visible inside procedures. There is a different namespace for variables and procedures, so you could have a procedure and a global variable with the same name without conflict. You can use the namespace facility described in Chapter 7 to manage procedures and global variables. Each procedure has a local scope for variables. That is, variables introduced in the procedure live only for the duration of the procedure call. After the procedure returns, those variables are undefined. Variables defined outside the procedure are not visible to a procedure unless the upvar or global scope commands are used. You can also use qualified names to name variables in a namespace scope. The global and upvar commands are described later in this chapter. Qualified names are described on page 198. If the same variable name exists in an outer scope, it is unaffected by the use of that variable name inside a procedure. In Example 7-3, the variable a in the global scope is different from the parameter a to P1. Similarly, the global variable b is different from the variable b inside P1: Example 7-3 Variable scope and Tcl procedures. set a 5 set b -8 proc P1 {a} { set b 42 if {$a < 0} { return $b } else { return $a } } P1 $b => 42 P1 [expr $a*2]
=> 10
Top
Table of Contents
The global command adds a global variable to the current scope. A common mistake is to have a single global command and expect that to apply to all procedures. However, a global command in the global scope has no effect. Instead, you must put a global command in all procedures that access the global variable. The variable can be undefined at the time the global command is used. When the variable is defined, it becomes visible in the global scope. Example 7-4 shows a random number generator. Before we look at the example, let me point out that the best way to get random numbers in Tcl is to use the rand() math function: expr rand() => .137287362934 The point of the example is to show a state variable, the seed, that has to persist between calls to random, so it is kept in a global variable. The choice of randomSeed as the name of the global variable associates it with the random number generator. It is important to pick names of global variables carefully to avoid conflict with other parts of your program. For comparison, Example 14-1 on page 196 uses namespaces to hide the state variable:
Example 7-4 A random number generator.[*] proc RandomInit { seed } { global randomSeed set randomSeed $seed } proc Random {} { global randomSeed set randomSeed [expr ($randomSeed*9301 + 49297) % 233280] return [expr $randomSeed/double(233280)] } proc RandomRange { range } { expr int([Random]*$range) } RandomInit [pid] => 5049 Random => 0.517686899863 Random => 0.217176783265 RandomRange 100 => 17
[*]
Adapted from Exploring Expect by Don Libes, O'Reilly & Associates, Inc., 1995, and from Numerical Recipes in C by Press et al., Cambridge University Press, 1988.
Top
Table of Contents
Example 7-6 Improved incr procedure. proc incr { varName {amount 1}} { upvar 1 $varName var if {[info exists var]} { set var [expr $var + $amount] } else { set var $amount } return $var }
Top
Table of Contents
Your code can pass name around as a handle on an object, then use upvar to get access to the data associated with the object. Your code is just written to use the state variable, which is an alias to the state variable for the current object. This technique is illustrated in Example 17-7 on page 232.
The aliases created with upvar do not work with these commands, nor do they work if you use trace, which is described on page 183. Instead, you must use the actual name of the global variable. To continue the above example where state is an alias, you cannot: vwait state(foo) button .b -textvariable state(foo) Instead, you must vwait state$name\(foo) button .b -textvariable state$name\(foo) The backslash turns off the array reference so Tcl does not try to access name as an array. You do not need to worry about special characters in $name, except parentheses. Once the name has been passed into the Tk widget it will be used directly as a variable name.
Top
Table of Contents
Top
Table of Contents
Array Syntax
The index of an array is delimited by parentheses. The index can have any string value, and it can be the result of variable or command substitution. Array elements are defined with set: set arr(index) value The value of an array element is obtained with $ substitution: set foo $arr(index) Example 8-1 uses the loop variable value $i as an array index. It sets arr(x) to the product of 1 * 2 * ... * x: Example 8-1 Using arrays. set arr(0) 1 for {set i 1} {$i <= 10} {incr i} { set arr($i) [expr {$i * $arr([expr $i-1])}] }
Complex Indices
An array index can be any string, like orange, 5, 3.1415, or foo,bar. The examples in this chapter, and in this book, often use indices that are pretty complex strings to create flexible data structures. As a rule of thumb, you can use any string for an index, but avoid using a string that contains spaces.
The main Tcl parser does not know about array syntax. All the rules about grouping and substitution described in Chapter 1 are still the same in spite of the array syntax described here. Parentheses do not group like curly braces or quotes, which is why a space causes problems. If you have complex indices, use a comma to separate different parts of the index. If you use a space in an index instead, then you have a quoting problem. The space in the index needs to be quoted with a backslash, or the whole variable reference needs to be grouped: set {arr(I'm asking for trouble)} {I told you so.} set arr(I'm\ asking\ for\ trouble) {I told you so.} If the array index is stored in a variable, then there is no problem with spaces in the variable's value. The following works well: set index {I'm asking for trouble} set arr($index) {I told you so.}
Array Variables
You can use an array element as you would a simple variable. For example, you can test for its existence with info exists, increment its value with incr, and append elements to it with lappend: if {[info exists stats($event)]} {incr stats($event)} You can delete an entire array, or just a single array element with unset. Using unset on an array is a convenient way to clear out a big data structure. It is an error to use a variable as both an array and a normal variable. The following is an error: set arr(0) 1 set arr 3 => can't set "arr": variable is array The name of the array can be the result of a substitution. This is a tricky situation, as shown in Example 8-2: Example 8-2 Referencing an array indirectly.
set name TheArray => TheArray set ${name}(xyz) {some value} => some value set x $TheArray(xyz) => some value set x ${name}(xyz) => TheArray(xyz) set x [set ${name}(xyz)] => some value A better way to deal with this situation is to use the upvar command, which is introduced on page 85. The previous example is much cleaner when upvar is used: Example 8-3 Referencing an array indirectly using upvar. set name TheArray => TheArray upvar 0 $name a set a(xyz) {some value} => some value set x $TheArray(xyz) => some value
Top
Table of Contents
Returns 1 if arr is an array variable. Returns a list that alternates between an index and the corresponding array value. pattern selects matching indices. If not specified, all indices and values are returned. Returns the list of all indices defined for arr, or those that match the string match pattern. Initializes the array arr from list, which has the same form as the list returned by array get. Returns the number of indices defined for arr. Returns a search token for a search through arr. Returns the value of the next element in array in the search identified by the token id. Returns an empty string if no more elements remain in the search. Returns 1 if more elements remain in the search. Ends the search identified by id.
array names arr ? pattern? array set arr list array size arr array startsearch arr array nextelement arr id array anymore arr id array donesearch arr id
Top
Table of Contents
A good use for arrays is to collect together a set of related variables for a module, much as one would use a record in other languages. By collecting these together in an array that has the same name as the module, name conflicts between different modules are avoided. Also, in each of the module's procedures, a single global statement will suffice to make all the state variables visible. You can also use upvar to manage a collection of arrays, as shown in Example 8-8 on page 95.
Simple Records
Suppose we have a database of information about people. One approach uses a different array for each class of information. The name of the person is the index into each array: Example 8-5 Using arrays for records, version 1. proc Emp_AddRecord {id name manager phone} { global employeeID employeeManager \ employeePhone employeeName set employeeID($name) $id set employeeManager($name) $manager set employeePhone($name) $phone
set employeeName($id) $name } proc Emp_Manager {name} { global employeeManager return $employeeManager($name) } Simple procedures are defined to return fields of the record, which hides the implementation so that you can change it more easily. The employeeName array provides a secondary key. It maps from the employee ID to the name so that the other information can be obtained if you have an ID instead of a name. Another way to implement the same little database is to use a single array with more complex indices: Example 8-6 Using arrays for records, version 2. proc Emp_AddRecord {id name manager phone} { global employee set employee(id,$name) $id set employee(manager,$name) $manager set employee(phone,$name) $phone set employee(name,$id) $name } proc Emp_Manager {name} { global employee return $employee(manager,$name) } The difference between these two approaches is partly a matter of taste. Using a single array can be more convenient because there are fewer variables to manage. In any case, you should hide the implementation in a small set of procedures.
A Stack
A stack can be implemented with either a list or an array. If you use a list, then the push and pop operations have a runtime cost that is proportional to the size of the stack. If the stack has a few elements this is fine. If there are a lot of items in a stack, you may wish to use arrays instead. Example 8-7 Using a list to implement a stack. proc Push { stack value } { upvar $stack list lappend list $value } proc Pop { stack } { upvar $stack list set value [lindex $list end]
set list [lrange $list 0 [expr [llength $list]-2]] return $value } In these examples, the name of the stack is a parameter, and upvar is used to convert that into the data used for the stack. The variable is a list in Example 8-7 and an array in Example 8-8. The user of the stack module does not have to know. The array implementation of a stack uses one array element to record the number of items in the stack. The other elements of the array have the stack values. The Push and Pop procedures both guard against a nonexistent array with the info exists command. When the first assignment to S(top) is done by Push, the array variable is created in the caller's scope. The example uses array indices in two ways. The top index records the depth of the stack. The other indices are numbers, so the construct $S($S(top)) is used to reference the top of the stack. Example 8-8 Using an array to implement a stack. proc Push { stack value } { upvar $stack S if {![info exists S(top)]} { set S(top) 0 } set S($S(top)) $value incr S(top) } proc Pop { stack } { upvar $stack S if {![info exists S(top)]} { return {} } if {$S(top) == 0} { return {} } else { incr S(top) -1 set x $S($S(top)) unset S($S(top)) return $x } }
A List of Arrays
Suppose you have many arrays, each of which stores some data, and you want to maintain an overall ordering among the data sets. One approach is to keep a Tcl list with the name of each array in order. Example 8-9 defines RecordInsert to add an array to the list, and an iterator function, RecordIterate, that applies a script to each array in order. The iterator uses upvar to make data an alias for the current array. The script is executed with eval, which is described in detail in Chapter 10. The Tcl commands in script can reference the arrays with the name data:
Example 8-9 A list of arrays. proc RecordAppend {listName arrayName} { upvar $listName list lappend list $arrayName } proc RecordIterate {listName script} { upvar $listName list foreach arrayName $list { upvar #0 $arrayName data eval $script } } Another way to implement this list-of-records structure is to keep references to the arrays that come before and after each record. Example 8-10 shows the insert function and the iterator function when using this approach. Once again, upvar is used to set up data as an alias for the current array in the iterator. In this case, the loop is terminated by testing for the existence of the next array. It is perfectly all right to make an alias with upvar to a nonexistent variable. It is also all right to change the target of the upvar alias. One detail that is missing from the example is the initialization of the very first record so that its next element is the empty string: Example 8-10 A list of arrays. proc RecordInsert {recName afterThis} { upvar $recName record $afterThis after set record(next) $after(next) set after(next) $recName } proc RecordIterate {firstRecord body} { upvar #0 $firstRecord data while {[info exists data]} { eval $body upvar #0 $data(next) data } }
The datablob might be a name, value list suitable for passing to array set, or simply a large chunk of text or binary data. One implementation of Db_Insert might just be: foreach key $keylist { lappend Db($key) $datablob } The problem with this approach is that it duplicates the data chunks under each key. A better approach is to use two arrays. One stores all the data chunks under a simple ID that is generated automatically. The other array stores the association between the keys and the data chunks. Example 8-11, which uses the namespace syntax described in Chapter 14, illustrates this approach. The example also shows how you can easily dump data structures by writing array set commands to a file, and then load them later with a source command: Example 8-11 A simple in-memory database. namespace eval db { variable data ;# Array of data blobs variable uid 0 ;# Index into data variable index ;# Cross references into data } proc db::insert {keylist datablob} { variable data variable uid variable index set data([incr uid]) $datablob foreach key $keylist { lappend index($key) $uid } } proc db::get {key} { variable data variable index set result {} if {![info exist index($key)]} { return {} } foreach uid $index($key) { lappend result $data($uid) } return $result } proc db::save {filename} { variable uid set out [open $filename w] puts $out [list namespace eval db \ [list variable uid $uid]] puts $out [list array set db::data [array get db::data]]
puts $out [list array set db::index [array get db::index]] close $out } proc db::load {filename} { source $filename }
Top
Table of Contents
Top
Table of Contents
other UNIX shell exec commands, the Tcl exec does not replace the current process with the new one. Instead, the Tcl library forks first and executes the program as a child process.
set d [exec date] The standard output of the program is returned as the value of the exec command. However, if the program writes to its standard error channel or exits with a nonzero status code, then exec raises an error. If you do not care about the exit status, or you use a program that insists on writing to standard error, then you can use catch to mask the errors: catch {exec program arg arg} result The exec command supports a full set of I/O redirection and pipeline syntax. Each process normally has three I/O channels associated with it: standard input, standard output, and standard error. With I/O redirection, you can divert these I/O channels to files or to I/O channels you have opened with the Tcl open command. A pipeline is a chain of processes that have the standard output of one command hooked up to the standard input of the next command in the pipeline. Any number of programs can be linked together into a pipeline. Example 9-1 Using exec on a process pipeline. set n [exec sort < /etc/passwd | uniq | wc -l 2> /dev/null] Example 9-1 uses exec to run three programs in a pipeline. The first program is sort, which takes its input from the file /etc/passwd. The output of sort is piped into uniq, which suppresses duplicate lines. The output of uniq is piped into wc, which counts the lines. The error output of the command is
diverted to the null device to suppress any error messages. Table 9-1 provides a summary of the syntax understood by the exec command.
(First argument.) Do not discard trailing newline from the result. Pipes standard output from one process into another. Pipes both standard output and standard error output. Takes input from the named file. Takes input from the I/O channel identified by fileId. Takes input from the given value. Overwrites fileName with standard output. Overwrites fileName with standard error output. Overwrites fileName with both standard error and standard out. Appends standard output to the named file. Appends standard error to the named file. Appends both standard error and standard output to the named file. Directs standard output to the I/O channel identified by fileId. Directs standard error to the I/O channel identified by fileId. Directs both standard error and standard output to the I/O channel. As the last argument, indicates pipeline should run in background.
A trailing & causes the program to run in the background. In this case, the process identifier is returned by the exec command. Otherwise, the exec command blocks during execution of the program, and the standard output of the program is the return value of exec. The trailing newline in the output is trimmed off, unless you specify -keepnewline as the first argument to exec. If you look closely at the I/O redirection syntax, you'll see that it is built up from a few basic building blocks. The basic idea is that | stands for pipeline, > for output, and < for input. The standard error is joined to the standard output by &. Standard error is diverted separately by using 2>. You can use your own I/O channels by using @.
instead of: exec ls This is handy if you are using the Tcl interpreter as a general shell. It can also cause unexpected behavior when you are just playing around. To turn this off, define the auto_noexec variable: set auto_noexec anything
Top
Table of Contents
Returns access time as a decimal string. Queries or sets file attributes. (Tcl 8.0) Copies file source to file destination. The source and destination can be directories. (Tcl 7.6) Deletes the named file. (Tcl 7.6) Returns parent directory of file name. Returns 1 if name has execute permission, else 0. Returns 1 if name exists, else 0. Returns the part of name from the last dot (i.e., .) to the end. The dot is included in the return value. Returns 1 if name is a directory, else 0. Returns 1 if name is not a directory, symbolic link, or device, else 0. Joins pathname components into a new pathname. (Tcl 7.5) Places attributes of the link name into var.
file mkdir name file mtime name file nativename name file owned name file pathtype name file readable name file readlink name file rename ?-force? old new file rootname name file size name file split name file stat name var file tail name file type name file writable name
Creates directory name. (Tcl 7.6) Returns modify time of name as a decimal string. Returns the platform-native version of name. (Tk 8.0). Returns 1 if current user owns the file name, else 0.
relative, absolute,
Returns 1 if name has read permission, else 0. Returns the contents of the symbolic link name. Changes the name of old to new. (Tcl 7.6) Returns all but the extension of name (i.e., up to but not including the last . in name). Returns the number of bytes in name. Splits name into its pathname components. (Tcl 7.5) Places attributes of name into array var. The elements defined for var are listed in Table 9-3. Returns the last pathname component of name. Returns type identifier, which is one of: file, directory, characterSpecial, blockSpecial , fifo, link, or socket. Returns 1 if name has write permission, else 0.
Top
Table of Contents
syntax for the current platform. You can construct these names with file join described later. You can also convert a UNIX-like name to a native name with file nativename. Several of the file operations operate on pathnames as opposed to returning information about the file itself. You can use the dirname, extension, join, pathtype, rootname, split, and tail operations on any string; there is no requirement that the pathnames refer to an existing file.
The platform-independent way to construct file names is with file join. The following command returns the name of the init.tcl file in native format: set file [file join $tcl_library init.tcl] The file join operation can join any number of pathname components. In addition, it has the feature that an absolute pathname overrides any previous components. For example (on UNIX), /b/c is an absolute pathname, so it overrides any paths that come before it in the arguments to file join: file join a b/c d => a/b/c/d file join a /b/c d => /b/c/d On Macintosh, a relative pathname starts with a colon, and an absolute pathname does not. To specify an absolute path, you put a trailing colon on the first component so that it is interpreted as a volume specifier. These relative components are joined into a relative pathname: file join a :b:c d
=> :a:b:c:d In the next case, b:c is an absolute pathname with b: as the volume specifier. The absolute name overrides the previous relative name: file join a b:c d => b:c:d The file join operation converts UNIX-style pathnames to native format. For example, on Macintosh you get this: file join /usr/local/lib => usr:local:lib
Top
Table of Contents
Copying Files
The file copy operation copies files and directories. The following example copies file1 to file2. If file2 already exists, the operation raises an error unless the -force option is specified: file copy ?-force? file1 file2 Several files can be copied into a destination directory. The names of the source files are preserved. The -force option indicates that files under directory can be replaced: file copy ?-force? file1 file2 ... directory Directories can be recursively copied. The -force option indicates that files under dir2 can be replaced: file copy ?-force? dir1 dir2
Creating Directories
The file mkdir operation creates one or more directories: file mkdir dir dir ... It is not an error if the directory already exists. Furthermore, intermediate directories are created if needed. This means that you can always make sure a directory exists with a single mkdir operation. Suppose /tmp has no subdirectories at all. The following command creates /tmp/sub1 and /tmp/sub1/sub2: file mkdir /tmp/sub1/sub2 The -force option is not understood by file mkdir, so the following command -accidentally creates a folder named -force, as well as one named oops. file mkdir -force oops
Deleting Files
The file delete operation deletes files and directories. It is not an error if the files do not exist. A non-empty directory is not deleted unless the -force option is specified, in which case it is recursively deleted: file delete ?-force? name name ... To delete a file or directory named -force, you must specify a nonexistent file before the -force to prevent it from being interpreted as a flag (-force -force won't work): file delete xyzzy -force
ensures that any other programs that access the file will not see the new version until it is complete.
Top
Table of Contents
File Attributes
There are several file operations that return specific file attributes: atime, executable, exists, isdirectory, isfile, mtime, owned, readable, readlink, size and type. Refer to Table 9-2 on page 102 for their function. The following command uses file mtime to compare the modify times of two files. If you have ever resorted to piping the results of ls -l into awk in order to derive this information in other shell scripts, you will appreciate this example: Example 9-2 Comparing file modify times. proc newer { file1 file2 } { if ![file exists $file2] { return 1 } else { # Assume file1 exists expr [file mtime $file1] > [file mtime $file2] } } The stat and lstat operations return a collection of file attributes. They take a third argument that is the name of an array variable, and they initialize that array with elements that contain the file attributes. If the file is a symbolic link, then the lstat operation returns information about the link itself and the stat operation returns information about the target of the link. The array elements are listed in Table 9-3. All the element values are decimal strings, except for type, which can have the values returned by the type option. The element names are based on the UNIX stat system call. Use the file attributes command described later to get other platform-specific attributes:
atime ctime dev gid ino mode mtime nlink size type uid
The last access time, in seconds. The last change time (not the create time), in seconds. The device identifier, an integer. The group owner, an integer. The file number (i.e., inode number), an integer. The permission bits. The last modify time, in seconds. The number of links, or directory references, to the file. The number of bytes in the file.
file, directory, characterSpecial, blockSpecial , fifo, link,
or socket.
Example 9-3 uses the device (dev) and inode (ino) attributes of a file to determine whether two pathnames reference the same file. The attributes are UNIX specific; they are not well defined on Windows and Macintosh. Example 9-3 Determining whether pathnames reference the same file. proc fileeq { path1 path2 } { file stat $path1 stat1 file stat $path2 stat2 expr $stat1(ino) == $stat2(ino) && \ $stat1(dev) == $stat2(dev) } The file attributes operation was added in Tcl 8.0 to provide access to platform-specific attributes. The attributes operation lets you set and query attributes. The interface uses option-value pairs. With no options, all the current values are returned. file attributes book.doc => -creator FRAM -hidden 0 -readonly 0 -type MAKR These Macintosh attributes are explained in Table 9-4. The four-character type codes used on Macintosh are illustrated on page 516. With a single option, only that value is returned: file attributes book.doc -readonly => 0 The attributes are modified by specifying one or more optionvalue pairs. Setting attributes can raise
an error if you do not have the right permissions: file attributes book.doc -readonly 1 -hidden 0
File permission bits. mode is a number with bits defined by the chmod system call. (UNIX) The group owner of the file. (UNIX) The owner of the file. (UNIX) The archive bit, which is set by backup programs. (Windows) If set, then the file does not appear in listings. (Windows, Macintosh) If set, then you cannot write the file. (Windows, Macintosh) If set, then you cannot remove the file. (Windows)
type is 4-character code of creating application. (Macintosh) type is 4-character type code. (Macintosh)
Top
Table of Contents
Returns channel ID for a file or pipeline. Writes a string. Reads a line. Reads numBytes bytes, or all data. Reads all bytes and discard the last \n. Returns the seek offset. Sets the seek offset. origin is one of start, current, or end. Queries end-of-file status. Writes buffers of a channel. Closes an I/O channel.
Top
Table of Contents
Opens for reading. The file must exist. Opens for reading and writing. The file must exist. Opens for writing. Truncate if it exists. Create if it does not exist. Opens for reading and writing. Truncate or create. Opens for writing. Data is appended to the file. Opens for reading and writing. Data is appended.
Opens for reading. Opens for writing. Opens for reading and writing. Opens for append. Creates the file if it does not exist. If CREAT is also specified, then the file cannot already exist. Prevents terminal devices from becoming the controlling terminal. Does not block during the open. Truncates the file if it exists.
The permissions argument is a value used for the permission bits on a newly created file. UNIX uses three bits each for the owner, group, and everyone else. The bits specify read, write, and execute permission. These bits are usually specified with an octal number, which has a leading zero, so that there is one octal digit for each set of bits. The default permission bits are 0666, which grant read/write access to everybody. Example 9-4 specifies 0600 so that the file is readable and writable only by the owner. 0775 would grant read, write, and execute permissions to the owner and group, and read and execute permissions to everyone else. You can set other special properties with additional high-order bits. Consult the UNIX manual page on chmod command for more details. The following example illustrates how to use a list of POSIX access flags to open a file for reading and writing, creating it if needed, and not truncating it. This is something you cannot do with the simpler form of the access argument: set fileId [open /tmp/bar {RDWR CREAT}] Catch errors from open.
In general, you should check for errors when opening files. The following example illustrates a catch phrase used to open files. Recall that catch returns 1 if it catches an error; otherwise, it returns zero. It treats its second argument as the name of a variable. In the error case, it puts the error message into the variable. In the normal case, it puts the result of the command into the variable: Example 9-5 A more careful use of open.
if [catch {open /tmp/data r}fileId] { puts stderr "Cannot open /tmp/data: $fileId" } else { # Read and process the file, then... close $fileId }
Expect
If you are trying to do sophisticated things with an external application, you will find that the Expect extension provides a much more powerful interface than a process pipeline. Expect adds Tcl commands that are used to control interactive applications. It is extremely useful for automating FTP, Telnet, and programs under test. It comes as a Tcl shell named expect, and it is also an extension that you can dynamically load into other Tcl shells. It was created by Don Libes at the National Institute of Standards and Technology (NIST). Expect is described in Exploring Expect (Libes, O'Reilly & Associates, Inc., 1995). You can find the software on the CD and on the web at: http://expect.nist.gov/
Top
Table of Contents
Example 9-8 A read loop using gets. while {[gets $channel line] >= 0} { # Process line } close $channel
To suppress conversions, use the fconfigure command, which is described in more detail on page 223. Example 9-10 demonstrates a File_Copy procedure that translates files to native format. It is complicated because it handles directories: Example 9-10 Copy a file and translate to native format. proc File_Copy {src dest} { if [file isdirectory $src] { file mkdir $dest foreach f [glob -nocomplain [file join $src *]] { File_Copy $f [file join $dest [file tail $f]] } return } if [file isdirectory $dest] { set dest [file join $dest [file tail $src]] } set in [open $src] set out [open $dest w] puts -nonewline $out [read $in] close $out ; close $in }
If the channel was a process pipeline and any of the processes wrote to their standard error channel, then Tcl believes this is an error. The error is raised when the channel to the pipeline is finally closed. Similarly, if any of the processes in the pipeline exit with a nonzero status, close raises an error.
Top
Table of Contents
Top
Table of Contents
matches zero or more characters. matches a single character. matches a set of characters. or c.
[abc]
All other characters must match themselves. The -nocomplain flag causes glob to return an empty list if no files match the pattern. Otherwise, glob raises an error if no files match. The -- flag must be used if the pattern begins with a -. Unlike the glob matching in csh, the Tcl glob command matches only the names of existing files. In csh, the {a,b} construct can match nonexistent names. In addition, the results of glob are not sorted. Use the lsort command to sort its result if you find it important. Example 9-11 shows the FindFile procedure, which traverses the file system hierarchy using recursion. At each iteration it saves its current directory and then attempts to change to the next subdirectory. A catch guards against bogus names. The glob command matches file names: Example 9-11 Finding a file by name.
proc FindFile { startDir namePat } { set pwd [pwd] if [catch {cd $startDir}err] { puts stderr $err return } foreach match [glob -nocomplain -- $namePat]{ puts stdout [file join $startDir $match] } foreach file [glob -nocomplain *] { if [file isdirectory $file] { FindFile [file join $startDir $file] $namePat } } cd $pwd }
expands to the current user's home directory. expands to the home directory of user.
~user
If you have a file that starts with a literal tilde, you can avoid the tilde expansion by adding a leading ./ (e.g., ./~foobar).
Top
Table of Contents
Top
Table of Contents
Environment Variables
Environment variables are a collection of string-valued variables associated with each process. The process's environment variables are available through the global array env. The name of the environment variable is the index, (e.g., env(PATH)), and the array element contains the current value of the environment variable. If assignments are made to env, they result in changes to the corresponding environment variable. Environment variables are inherited by child processes, so programs run with the exec command inherit the environment of the Tcl script. The following example prints the values of environment variables. Example 9-12 Printing environment variable values. proc printenv { args } { global env set maxl 0 if {[llength $args] == 0} { set args [lsort [array names env]] } foreach x $args { if {[string length $x] > $maxl} { set maxl [string length $x] } } incr maxl 2 foreach x $args { puts stdout [format "%*s = %s" $maxl $x $env($x)] } } printenv USER SHELL TERM => USER = welch SHELL = /bin/csh TERM = tx
Note: Environment variables can be initialized for Macintosh applications by editing a resource of type STR# whose name is Tcl Environment Variables. This resource is part of the tclsh and wish applications. Follow the directions on page 28 for using ResEdit. The format of the resource values is NAME=VALUE.
Top
Table of Contents
registry delete key ? valueName? registry get key valueName registry keys key ?pat? registry set key registry set key valueName data ?type? registry type key valueName registry values key ?pat?
Deletes the key and the named value, or it deletes all values under the key if valueName is not specified. Returns the value associated with valueName under key. Returns the list of keys or value names under key that match pat, which is a string match pattern. Creates key. Creates valueName under key with value data of the given type. Types are listed in Table 9-9. Returns the type of valueName under key. Returns the names of the values stored under key that match pat, which is a string match pattern. Table 9-9. The registry data types.
Arbitrary binary data. Arbitrary binary data. A string that contains references to environment variables with the %VARNAME% syntax. A 32-bit integer. A 32-bit integer in the other byte order. It is represented in Tcl as a decimal string. A symbolic link. An array of strings, which are represented as a Tcl list. A device driver resource list.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
The root of the quoting problems is the internal use of concat by eval and similar commands to concatenate their arguments into one command string. The concat can lose some important list structure so that arguments are not passed through as you expect. The general strategy to avoid these problems is to use list and lappend to explicitly form the command callback as a single, wellstructured list.
set cmd {puts stdout "Hello, World!"} => puts stdout "Hello, World!" # sometime later... eval $cmd => Hello, World! In this case, the value of cmd is passed to Tcl. All the standard grouping and substitution are done again on the value, which is a puts command. However, suppose that part of the command is stored in a variable, but that variable will not be defined at the time eval is used. We can artificially create this situation like this: set string "Hello, World!" set cmd {puts stdout $string} => puts stdout $string unset string eval $cmd => can't read "string": no such variable In this case, the command contains $string. When this is processed by eval, the interpreter looks for the current value of string, which is undefined. This example is contrived, but the same problem occurs if string is a local variable, and cmd will be evaluated later in the global scope. A common mistake is to use double quotes to group the command. That will let $string be substituted now. However, this works only if string has a simple value, but it fails if the value of string contains spaces or other Tcl special characters: set cmd "puts stdout $string" => puts stdout Hello, World! eval $cmd => bad argument "World!": should be "nonewline" The problem is that we have lost some important structure. The identity of $string as a single argument gets lost in the second round of parsing by eval. The solution to this problem is to construct the command using list, as shown in the following example: Example 10-1 Using list to construct commands. set string "Hello, World!" set cmd [list puts stdout $string] => puts stdout {Hello, World!} unset string eval $cmd => Hello, World!
The trick is that list has formed a list containing three elements: puts, stdout, and the value of string. The substitution of $string occurs before list is called, and list takes care of grouping that value for us. In contrast, using double quotes is equivalent to: set cmd [concat puts stdout $string] Double quotes lose list structure.
The problem here is that concat does not preserve list structure. The main lesson is that you should use list to construct commands if they contain variable values or command results that must be substituted now. If you use double quotes, the values are substituted but you lose proper command structure. If you use curly braces, then values are not substituted until later, which may not be in the right context.
The general strategy of passing out a command or script to call later is a flexible way to assemble different parts of an application, and it is widely used by Tcl commands. Examples include commands that are called when users click on Tk buttons, commands that are called when I/O channels have data ready, or commands that are called when clients connect to network servers. It is also easy to write your own procedures or C extensions that accept scripts and call them later in response to some event. These other callback situations may not appear to have the "concat problem" because they take a single script argument. However, as soon as you use double quotes to group that argument, you have created the concat problem all over again. So, all the caveats about using list to construct these commands still apply.
Suppose you want to create a whole procedure dynamically. Unfortunately, this can be particularly awkward because a procedure body is not a simple list. Instead, it is a sequence of commands that are each lists, but they are separated by newlines or semicolons. In turn, some of those commands may be loops and if commands that have their own command bodies. To further compound the problem, you typically have two kinds of variables in the procedure body: some that are to be used as values when constructing the body, and some that are to be used later when executing the procedure. The result can be very messy. The main trick to this problem is to use either format or regsub to process a template for your dynamically generated procedure. If you use format, then you can put %s into your templates where you want to insert values. You may find the positional notation of the format string (e.g., %1$s and %2$s) useful if you need to repeat a value in several places within your procedure body. The following example is a procedure that generates a new version of other procedures. The new version includes code that counts the number of times the procedure was called and measures the time it takes to run: Example 10-2 Generating procedures dynamically with a template. proc TraceGen {procName} { rename $procName $procName-orig set arglist {} foreach arg [info args $procName-orig] { append arglist "\$$arg " } proc $procName [info args $procName-orig] [format { global _trace_count _trace_msec incr _trace_count(%1$s) incr _trace_msec(%1$s) [lindex [time { set result [%1$s-orig %2$s] } 1] 0] return $result } $procName $arglist] } Suppose that we have a trivial procedure foo: proc foo {x y} { return [expr $x * $y] } If you run TraceGen on it and look at the results, you see this: TraceGen foo info body foo => global _trace_count _trace_msec incr _trace_count(foo)
incr _trace_msec(foo) [lindex [time { set result [foo-orig $x $y] }1] 0] return $result
Top
Table of Contents
Even though there are only two Tcl commands to create a user interface button, we will write a procedure that replaces the two commands with one. Our first version might be: proc PackedButton {name txt cmd} { button $name -text $txt -command $cmd pack $name -side left } This is not a very flexible procedure. The main problem is that it hides the full power of the Tk button command, which can really take about 20 widget configuration options, such as -background, cursor, -relief , and more. They are listed on page 391. For example, you can easily make a red button like this: button .foo -text Foo -command foo -background red A better version of PackedButton uses args to pass through extra configuration options to the button command. The args parameter is a list of all the extra arguments passed to the Tcl procedure. My first attempt to use $args looked like this, but it was not correct: proc PackedButton {name txt cmd args} { button $name -text $txt -command $cmd $args pack $name -side left } PackedButton .foo "Hello, World!" {exit} -background red => unknown option "-background red" The problem is that $args is a list value, and button gets the whole list as a single argument. Instead, button needs to get the elements of $args as individual arguments. Use eval with $args
In this case, you can use eval because it concatenates its arguments to form a single list before evaluation. The single list is, by definition, the same as a single Tcl command, so the button command parses correctly. Here we give eval two lists, which it joins into one command: eval {button $name -text $txt -command $cmd} $args The use of the braces in this command is discussed in more detail below. We also generalize our
procedure to take some options to the pack command. This argument, pack, must be a list of packing options. The final version of PackedButton is shown in Example 10-3: Example 10-3 Using eval with $args. # PackedButton creates and packs a button. proc PackedButton {path txt cmd {pack {-side right}} args} { eval {button $path -text $txt -command $cmd} $args eval {pack $path} $pack } In PackedButton, both pack and args are list-valued parameters that are used as parts of a command. The internal concat done by eval is perfect for this situation. The simplest call to PackedButton is: PackedButton .new "New" { New } The quotes and curly braces are redundant in this case but are retained to convey some type information. The quotes imply a string label, and the braces imply a command. The pack argument takes on its default value, and the args variable is an empty list. The two commands executed by PackedButton are: button .new -text New -command New pack .new -side right creates a horizontal stack of buttons by default. The packing can be controlled with a packing specification:
PackedButton
PackedButton .save "Save" { Save $file } {-side left} The two commands executed by PackedButton are: button .new -text Save -command { Save $file } pack .new -side left The remaining arguments, if any, are passed through to the button command. This lets the caller finetune some of the button attributes: PackedButton .quit Quit { Exit } {-side left -padx 5} \ -background red}
The two commands executed by PackedButton are: button .quit -text Quit -command { Exit }-background red pack .quit -side left -padx 5 You can see a difference between the pack and args argument in the call to PackedButton. You need to group the packing options explicitly into a single argument. The args parameter is automatically made into a list of all remaining arguments. In fact, if you group the extra button parameters, it will be a mistake: PackedButton .quit Quit { Exit } {-side left -padx 5} \ {-background red} => unknown option "-background red"
You may be tempted to use double quotes instead of curly braces in your uses of eval. Don't give in! Using double quotes is, mostly likely, wrong. Suppose the first eval command is written like this: eval "button $path -text $txt -command $cmd $args" Incidentally, the previous is equivalent to:
eval button $path -text $txt -command $cmd $args These versions happen to work with the following call because txt and cmd have one-word values with no special characters in them: PackedButton .quit Quit { Exit } The button command that is ultimately evaluated is: button .quit -text Quit -command { Exit } In the next call, an error is raised: PackedButton .save "Save As" [list Save $file] => unknown option "As" This is because the button command is this: button .save -text Save As -command Save /a/b/c But it should look like this instead: button .save -text {Save As}-command {Save /a/b/c} The problem is that the structure of the button command is now wrong. The value of txt and cmd are substituted first, before eval is even called, and then the whole command is parsed again. The worst part is that sometimes using double quotes works, and sometimes it fails. The success of using double quotes depends on the value of the parameters. When those values contain spaces or special characters, the command gets parsed incorrectly. Braces: the one true way to group arguments to eval.
eval {button $path -text $txt -command $cmd} $args The following variations are also correct. The first uses list to do quoting automatically, and the others use backslashes or braces to prevent the extra round of substitutions: eval [list button $path -text $txt -command $cmd] $args eval button \$path -text \$txt -command \$cmd $args eval button {$path} -text {$txt} -command {$cmd} $args Finally, here is one more incorrect approach that tries to quote by hand: eval "button {$path}-text {$txt}-command {$cmd} $args" The problem above is that quoting is not always done with curly braces. If a value contains an unmatched curly brace, Tcl would have used backslashes to quote it, and the above command would raise an error: set blob "foo\{bar space" => foo{bar space eval "puts {$blob}" => missing close brace eval puts {$blob} => foo{bar space
Top
Table of Contents
It is common to have the command in a variable. This is the case when the command has been passed into your new control flow procedure as an argument. In this case, you should evaluate the command one level up. Put the level in explicitly to avoid cases where $cmd looks like a number! uplevel 1 $cmd Another common scenario is reading commands from users as part of an application. In this case, you should evaluate the command at the global scope. Example 16-2 on page 220 illustrates this use of uplevel : uplevel #0 $cmd If you are assembling a command from a few different lists, such as the args parameter, then you can use concat to form the command: uplevel [concat $cmd $args] The lists in $cmd and $args are concatenated into a single list, which is a valid Tcl command. Like eval, uplevel uses concat internally if it is given extra arguments, so you can leave out the explicit use of concat. The following commands are equivalent: uplevel [concat $cmd $args] uplevel "$cmd $args" uplevel $cmd $args Example 10-4 shows list assignment using the foreach trick described on Page 75. List assignment is useful if a command returns several values in a list. The lassign procedure assigns the list elements to several variables. The lassign procedure hides the foreach trick, but it must use the uplevel command so that the loop variables get assigned in the correct scope. The list command is used to construct the foreach command that is executed in the caller's scope. This is necessary so that $variables and $values get substituted before the command is evaluated in the other scope. Example 10-4 lassign: list assignment with foreach. # # # # Assign a set of variables from a list of values. If there are more values than variables, they are returned. If there are fewer values than variables, the variables get the empty string.
error "wrong # args: lassign list varname ?varname..?" } if {[llength $valueList] == 0} { # Ensure one trip through the foreach loop set valueList [list {}] } uplevel 1 [list foreach $args $valueList {break}] return [lrange $valueList [llength $args] end] } Example 10-5 illustrates a new control structure with the File_Process procedure that applies a callback to each line in a file. The call to uplevel allows the callback to be concatenated with the line to form the command. The list command is used to quote any special characters in line, so it appears as a single argument to the command. Example 10-5 The File_Process procedure applies a command to each line of a file. proc File_Process {file callback} { set in [open $file] while {[gets $file line] >= 0} { uplevel 1 $callback [list $line] } close $in } What is the difference between these two commands? uplevel 1 [list $callback $line] uplevel 1 $callback [list $line] The first form limits callback to be the name of the command, while the second form allows callback to be a command prefix. Once again, what is the bug with this version? uplevel 1 $callback $line The arbitrary value of $line is concatenated to the callback command, and it is likely to be a malformed command when executed.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
One of the stumbling blocks with regular expressions is that they use some of the same special characters as Tcl. Any pattern that contains brackets, dollar signs, or spaces must be quoted when used
in a Tcl command. In many cases you can group the regular expression with curly braces, so Tcl pays no attention to it. However, when using Tcl 8.0 (or earlier) you may need Tcl to do backslash substitutions on part of the pattern, and then you need to worry about quoting the special characters in the regular expression. Advanced regular expressions eliminate this problem because backslash substitution is now done by the regular expression engine. Previously, to get \n to mean the newline character (or \t for tab) you had to let Tcl do the substitution. With Tcl 8.1, \n and \t inside a regular expression mean newline and tab. In fact, there are now about 20 backslash escapes you can use in patterns. Now more than ever, remember to group your patterns with curly braces to avoid conflicts between Tcl and the regular expression engine. The patterns in the first sections of this Chapter ignore this problem. The sample expressions in Table 11-7 on page 151 are quoted for use within Tcl scripts. Most are quoted simply by putting the whole pattern in braces, but some are shown without braces for comparison.
Top
Table of Contents
Matching Characters
Most characters simply match themselves. The following pattern matches an a followed by a b: ab The general wild-card character is the period, ".". It matches any single character. The following pattern matches an a followed by any character: a. Remember that matches can occur anywhere within a string; a pattern does not have to match the
whole string. You can change that by using anchors, which are described on page 137.
Character Sets
The matching character can be restricted to a set of characters with the [xyz] syntax. Any of the characters between the two brackets is allowed to match. For example, the following matches either Hello or hello: [Hh]ello The matching set can be specified as a range over the character set with the [x-y] syntax. The following matches any digit: [0-9] There is also the ability to specify the complement of a set. That is, the matching character can be anything except what is in the set. This is achieved with the [^xyz] syntax. Ranges and complements can be combined. The following matches anything except the uppercase and lowercase letters: [^a-zA-Z] Using special characters in character sets.
If you want a ] in your character set, put it immediately after the initial opening bracket. You do not need to do anything special to include [ in your character set. The following matches any square brackets or curley braces: [][{}] Most regular expression syntax characters are no longer special inside character sets. This means you do not need to backslash anything inside a bracketed character set except for backslash itself. The following pattern matches several of the syntax characters used in regular expressions: [][+*?()|\\]
Advanced regular expressions add names and backslash escapes as shorthand for common sets of characters like white space, alpha, alphanumeric, and more. These are described on page 139 and listed in Table 11-3 on page 146.
Quantifiers
Repetition is specified with *, for zero or more, +, for one or more, and ?, for zero or one. These quantifiers apply to the previous item, which is either a matching character, a character set, or a subpattern grouped with parentheses. The following matches a string that contains b followed by zero or more a's: ba* You can group part of the pattern with parentheses and then apply a quantifier to that part of the pattern. The following matches a string that has one or more sequences of ab: (ab)+ The pattern that matches anything, even the empty string, is: .* These quantifiers have a greedy matching behavior: They match as many characters as possible. Advanced regular expressions add nongreedy matching, which is described on page 140. For example, a pattern to match a single line might look like this: .*\n However, as a greedy match, this will match all the lines in the input, ending with the last newline in the input string. The following pattern matches up through the first newline. [^\n]*\n We will shorten this pattern even further on page 140 by using nongreedy quantifiers. There are also special newline sensitive modes you can turn on with some options described on page 143.
Alternation
Alternation lets you test more than one pattern at the same time. The matching engine is designed to be
able to test multiple patterns in parallel, so alternation is efficient. Alternation is specified with |, the pipe symbol. Another way to match either Hello or hello is: hello|Hello You can also write this pattern as: (h|H)ello or as: [hH]ello
Anchoring a Match
By default a pattern does not have to match the whole string. There can be unmatched characters before and after the match. You can anchor the match to the beginning of the string by starting the pattern with ^, or to the end of the string by ending the pattern with $. You can force the pattern to match the whole string by using both. All strings that begin with spaces or tabs are matched with: ^[ \t]+ If you have many text lines in your input, you may be tempted to think of ^ as meaning "beginning of line" instead of "beginning of string." By default, the ^ and $ anchors are relative to the whole input, and embedded newlines are ignored. Advanced regular expressions support options that make the ^ and $ anchors line-oriented. They also add the \A and \Z anchors that always match the beginning and end of the string, respectively.
Backslash Quoting
Use the backslash character to turn off these special characters : . * ? + [ ] ( ) ^ $ | \ For example, to match the plus character, you will need: \+
Remember that this quoting is not necessary inside a bracketed expression (i.e., a character set definition.) For example, to match either plus or question mark, either of these patterns will work: (\+|\?) [+?] To match a single backslash, you need two. You must do this everywhere, even inside a bracketed expression. Or you can use \B, which was added as part of advanced regular expressions. Both of these match a single backslash: \\ \B Unknown backslash sequences are an error.
Versions of Tcl before 8.1 ignored unknown backslash sequences in regular expressions. For example, \= was just =, and \w was just w. Even \n was just n, which was probably frustrating to many beginners trying to get a newline into their pattern. Advanced regular expressions add backslash sequences for tab, newline, character classes, and more. This is a convenient improvement, but in rare cases it may change the semantics of a pattern. Usually these cases are where an unneeded backslash suddenly takes on meaning, or causes an error because it is unknown.
Matching Precedence
If a pattern can match several parts of a string, the matcher takes the match that occurs earliest in the input string. Then, if there is more than one match from that same point because of alternation in the pattern, the matcher takes the longest possible match. The rule of thumb is: first, then longest. This rule gets changed by nongreedy quantifiers that prefer a shorter match. Watch out for *, which means zero or more, because zero of anything is pretty easy to match. Suppose your pattern is: [a-z]* This pattern will match against 123abc, but not how you expect. Instead of matching on the letters in the string, the pattern will match on the zero-length substring at the very beginning of the input string! This behavior can be seen by using the -indices option of the regexp command described on page 148. This option tells you the location of the matching string instead of the value of the matching string.
Capturing Subpatterns
Use parentheses to capture a subpattern. The string that matches the pattern within parentheses is remembered in a matching variable, which is a Tcl variable that gets assigned the string that matches the pattern. Using parentheses to capture subpatterns is very useful. Suppose we want to get everything between the <td> and </td> tags in some HTML. You can use this pattern: <td>([^<]*)</td> The matching variable gets assigned the part of the input string that matches the pattern inside the parentheses. You can capture many subpatterns in one match, which makes it a very efficient way to pick apart your data. Matching variables are explained in more detail on page 148 in the context of the regexp command. Sometimes you need to introduce parentheses but you do not care about the match that occurs inside them. The pattern is slightly more efficient if the matcher does not need to remember the match. Advanced regular expressions add noncapturing parentheses with this syntax: (?:pattern)
Top
Table of Contents
Character Classes
Character classes are names for sets of characters. The named character class syntax is valid only inside a bracketed character set. The syntax is [:identifier:] For example, alpha is the name for the set of uppercase and lowercase letters. The following two patterns are almost the same: [A-Za-z] [[:alpha:]] The difference is that the alpha character class also includes accented characters like . If you match data that contains nonASCII characters, the named character classes are more general than trying to name the characters explicitly. There are also backslash sequences that are shorthand for some of the named character classes. The following patterns to match digits are equivalent: [0-9] [[:digit:]] \d The following patterns match space-like characters including backspace, form feed, newline, carriage return, tag, and vertical tab:
[ \b\f\n\r\t\v] [:space:] \s The named character classes and the associated backslash sequence are listed in Table 11-3 on page 146. You can use character classes in combination with other characters or character classes inside a character set definition. The following patterns match leters, digits, and underscore: [[:digit:][:alpha:]_] [\d[:alpha:]_] [[:alnum:]_] \w Note that \d, \s and \w can be used either inside or outside character sets. When used outside a bracketed expression, they form their own character set. There are also \D, \S, and \W, which are the complement of \d, \s, and \w. These escapes (i.e., \D for not-a-digit) cannot be used inside a bracketed character set. There are two special character classes, [[:<:] and [[:>:]], that match the beginning and end of a word, respectively. A word is defined as one or more characters that match \w.
nongreedy Quantifiers
The *, +, and ? characters are quantifiers that specify repetition. By default these match as many characters as possible, which is called greedy matching. A nongreedy match will match as few characters as possible. You can specify nongreedy matching by putting a question mark after these quantifiers. Consider the pattern to match "one or more of not-a-newline followed by a newline." The not-a-newline must be explicit with the greedy quantifier, as in: [^\n]+\n Otherwise, if the pattern were just .+\n then the "." could well match newlines, so the pattern would greedily consume everything until the very last newline in the input. A nongreedy match would be satisfied with the very first newline instead:
.+?\n By using the nongreedy quantifier we've cut the pattern from eight characters to five Another example that is shorter with a nongreedy quantifier is the HTML example from page 138. The following pattern also matches everything between <td> and </td>: <td>(.*?)</td> Even ? can be made nongreedy, ??, which means it prefers to match zero instead of one. This only makes sense inside the context of a larger pattern. Send me e-mail if you have a compelling example for it!
Bound Quantifiers
The {m,n} syntax is a quantifier that means match at least m and at most n of the previous matching item. There are two variations on this syntax. A simple {m} means match exactly m of the previous matching item. A {m,} means match m or more of the previous matching item. All of these can be made nongreedy by adding a ? after them.
Back References
A back reference is a feature you cannot easily get with basic regular expressions. A back reference matches the value of a subpattern captured with parentheses. If you have several sets of parentheses you can refer back to different captured expressions with \1, \2, and so on. You count by left parentheses to determine the reference. For example, suppose you want to match a quoted string, where you can use either single or double quotes. You need to use an alternation of two patterns to match strings that are enclosed in double quotes or in single quotes: ("[^"]*"|'[^']*') With a back reference, \1, the pattern becomes simpler: ('|").*?\1 The first set of parenthesis matches the leading quote, and then the \1 refers back to that particular quote character. The nongreedy quantifier ensures that the pattern matches up to the first occurrence of the matching quote.
Look-ahead
Look-ahead patterns are subexpressions that are matched but do not consume any of the input. They act like constraints on the rest of the pattern, and they typically occur at the end of your pattern. A positive look-ahead causes the pattern to match if it also matches. A negative look-ahead causes the pattern to match if it would not match. These constraints make more sense in the context of matching variables and in regular expression subsitutions done with the regsub command. For example, the following pattern matches a filename that begins with A and ends with .txt ^A.*\.txt$ The next version of the pattern adds parentheses to group the file name suffix. ^A.*(\.txt)$ The parentheses are not strictly necessary, but they are introduced so that we can compare the pattern to one that uses look-ahead. A version of the pattern that uses look-ahead looks like this: ^A.*(?=\.txt)$ The pattern with the look-ahead constraint matches only the part of the filename before the .txt, but only if the .txt is present. In other words, the .txt is not consumed by the match. This is visible in the value of the matching variables used with the regexp command. It would also affect the substitutions done in the regsub command. There is negative look-ahead too. The following pattern matches a filename that begins with A and does not end with .txt. ^A.*(?!\.txt)$ Writing this pattern without negative look-ahead is awkward.
Character Codes
The \nn and \mmm syntax, where n and m are digits, can also mean an 8-bit character code corresponding to the octal value nn or mmm. This has priority over a back reference. However, I just wouldn't use this notation for character codes. Instead, use the Unicode escape sequence, \unnnn, which specifies a 16-bit value. The \xnn sequence also specifies an 8-bit character code. Unfortunately, the \x escape consumes all hex digits after it (not just two!) and then truncates the hexadecimal value down to 8 bits. This misfeature of \x is not considered a bug and will probably not change even in future versions of Tcl. The \Uyyyyyyyy syntax is reserved for 32-bit Unicode, but I don't expect to see that implemented anytime soon.
Collating Elements
Collating elements are characters or long names for characters that you can use inside character sets. Currently, Tcl only has some long names for various ASCII punctuation characters. Potentially, it could support names for every Unicode character, but it doesn't because the mapping tables would be huge. This section will briefly mention the syntax so that you can understand it if you see it. But its usefulness is still limited. Within a bracketed expression, the following syntax is used to specify a collating element: [.identifier.] The identifier can be a character or a long name. The supported long names can be found in the generic/regc_locale.c file in the Tcl source code distribution. A few examples are shown below: [.c.] [.#.] [.number-sign.]
Equivalence Classes
An equivalence class is all characters that sort to the same position. This is another feature that has limited usefulness in the current version of Tcl. In Tcl, characters sort by their Unicode character value, so there are no equivalence classes that contain more than one character! However, you could imagine a character class for 'o', '', and other accented versions of the letter o. The syntax for equivalence classes within bracketed expressions is: [=char=] where char is any one of the characters in the character class. This syntax is valid only inside a character class definition.
The linestop option prevents . (i.e., period) and character sets that begin with ^ from matching a newline character. In otherwords, unless you explicitly include \n in your pattern, it will not match across newlines.
Embedded Options
You can start a pattern with embedded options to turn on or off case sensitivity, newline sensitivity, and expanded syntax, which is explained in the next section. You can also switch from advanced regular expressions to a literal string, or to older forms of regular expressions. The syntax is a leading: (?chars) where chars is any number of option characters. The option characters are listed in Table 11-5 on page 147.
Expanded Syntax
Expanded syntax lets you include comments and extra white space in your patterns. This can greatly improve the readability of complex patterns. Expanded syntax is turned on with a regexp command option or an embeded option. Comments start with a # and run until the end of line. Extra white space and comments can occur anywhere except inside bracketed expressions (i.e., character sets) or within multicharacter syntax elements like (?=. When you are in expanded mode, you can turn off the comment character or include an explicit space by preceeding them with a backslash. Example 11-1 shows a pattern to match URLs. The leading (?x) turns on expanded syntax. The whole pattern is grouped in curly braces to hide it from Tcl. This example is considered again in more detail in Example 11-3 on page 150: Example 11-1 Expanded regular expressions allow comments. regexp {(?x) ([^:]+): //([^:/]+) (:([0-9]+))? (/.*) } $input # # # # # A pattern to match URLS The protocol before the initial colon The server name The optional port number The trailing pathname
Top
Table of Contents
Syntax Summary
Table 11-1 summarizes the syntax of regular expressions available in all versions of Tcl:
Matches any character. Matches zero or more instances of the previous pattern item. Matches one or more instances of the previous pattern item. Matches zero or one instances of the previous pattern item. Groups a subpattern. The repetition and alternation operators apply to the preceding subpattern. Alternation. Delimit a set of characters. Ranges are specified as [x-y]. If the first character in the set is ^, then there is a match if the remaining characters in the set are not present. Anchor the pattern to the beginning of the string. Only when first. Anchor the pattern to the end of the string. Only when last.
Advanced regular expressions, which were introduced in Tcl 8.1, add more syntax that is summarized in Table 11-2:
{m} {m}? {m,} {m,}? {m,n} {m,n}? *? +? ?? (?:re) (?=re) (?!re) (?abc) \c [: :] [. .] [= =]
Matches m instances of the previous pattern item. Matches m instances of the previous pattern item. Nongreedy. Matches m or more instances of the previous pattern item. Matches m or more instances of the previous pattern item. Nongreedy. Matches m through n instances of the previous pattern item. Matches m through n instances of the previous pattern item. Nongreedy. Matches zero or more instances of the previous pattern item. Nongreedy. Matches one or more instances of the previous pattern item. Nongreedy. Matches zero or one instances of the previous pattern item. Nongreedy. Groups a subpattern, re, but does not capture the result. Positive look-ahead. Matches the point where re begins. Negative look-ahead. Matches the point where re does not begin. Embedded options, where abc is any number of option letters listed in Table 11-5. One of many backslash escapes listed in Table 11-4. Delimits a character class within a bracketed expression. See Table 11-3. Delimits a collating element within a bracketed expression. Delimits an equivalence class within a bracketed expression.
Table 11-3 lists the named character classes defined in advanced regular expressions and their associated backslash sequences, if any. Character class names are valid inside bracketed character sets with the [:class:] syntax.
alnum alpha blank cntrl digit graph lower print punct space upper xdigit
Upper and lower case letters and digits. Upper and lower case letters. Space and tab. Control characters: \u0001 through \u001F. The digits zero through nine. Also \d. Printing characters that are not in cntrl or space. Lowercase letters. The same as alnum. Punctuation characters. Space, newline, carrage return, tab, vertical tab, form feed. Also \s. Uppercase letters. Hexadecimal digits: zero through nine, a-f, A-F.
Alert, or "bell", character. Matches only at the beginning of the string. Backspace character, \u0008. Synonym for backslash. Control-X. Digits. Same as [[:digit:]] Not a digit. Same as [^[:digit:]] Escape character, \u001B. Form feed, \u000C. Matches the beginning of a word. Matches the end of a word. Newline, \u000A. Carriage return, \u000D. Space. Same as [[:space:]] Not a space. Same as [^[:space:]]
Horizontal tab, \u0009. A 16-bit Unicode character code. Vertical tab, \u000B. Letters, digit, and underscore. Same as [[:alnum:]_] Not a letter, digit, or underscore. Same as [^[:alnum:]_] An 8-bit hexidecimal character code. Consumes all hex digits after \x. Matches the beginning or end of a word. Matches a point that is not the beginning or end of a word. Matches the end of the string. NULL, \u0000 Where x is a digit, this is a back-reference. Where x and y are digits, either a decimal back-reference, or an 8-bit octal character code. Where x, y and z are digits, either a decimal back-reference or an 8-bit octal character code.
Table 11-5 lists the embeded option characters used with the (?abc) syntax.
Table 11-5. Embedded option characters used with the (?x) syntax.
b c e i m n p q s t w x
The rest of the pattern is a basic regular expression (a la vi or grep). Case sensitive matching. This is the default. The rest of the pattern is an extended regular expression (a la Tcl 8.0). Case insensitive matching. Synonym for the n option. Newline sensitive matching . Both lineanchor and linestop mode. Partial newline sensitive matching. Only linestop mode. The rest of the pattern is a literal string. No newline sensitivity. This is the default. Tight syntax; no embedded comments. This is the default. Inverse partial newline-sensitive matching. Only lineanchor mode. Expanded syntax with embeded white space and comments.
Top
Table of Contents
Lowercase characters in pattern can match either lowercase or uppercase letters in string. The match variables each contain a pair of numbers that are in indices delimiting the match within string. Otherwise, the matching string itself is copied into the match variables. The pattern uses the expanded syntax discussed on page 144. The same as specifying both -lineanchor and -linestop. Change the behavior of ^ and $ so they are line-oriented as discussed on page 143. Change matching so that . and character classes do not match newlines as discussed on page 143. Useful for debugging. It returns information about the pattern instead of trying to match it against the input. Signals the end of the options. You must use this if your pattern begins with -.
The pattern argument is a regular expression as described earlier. If string matches pattern, then the results of the match are stored in the variables named in the command. These match variable
arguments are optional. If present, match is set to be the part of the string that matched the pattern. The remaining variables are set to be the substrings of string that matched the corresponding subpatterns in pattern. The correspondence is based on the order of left parentheses in the pattern to avoid ambiguities that can arise from nested subpatterns. Example 11-2 uses regexp to pick the hostname out of the DISPLAY environment variable, which has the form: hostname:display.screen
Example 11-2 Using regular expressions to parse a string. set env(DISPLAY) sage:0.1 regexp {([^:]*):}$env(DISPLAY) match host => 1 set match => sage: set host => sage The pattern involves a complementary set, [^:], to match anything except a colon. It uses repetition, *, to repeat that zero or more times. It groups that part into a subexpression with parentheses. The literal colon ensures that the DISPLAY value matches the format we expect. The part of the string that matches the complete pattern is stored into the match variable. The part that matches the subpattern is stored into host. The whole pattern has been grouped with braces to quote the square brackets. Without braces it would be: regexp (\[^:\]*): $env(DISPLAY) match host With advanced regular expressions the nongreedy quantifier *? can replace the complementary set: regexp (.*?): $env(DISPLAY) match host This is quite a powerful statement, and it is efficient. If we had only had the string command to work with, we would have needed to resort to the following, which takes roughly twice as long to interpret: set i [string first : $env(DISPLAY)] if {$i >= 0} { set host [string range $env(DISPLAY) 0 [expr $i-1]] }
The port number is optional, so a subpattern is delimited with parentheses and followed by a question mark. An additional set of parentheses are added to capture the port number without the leading colon. This matches the :80 part of the URL: (:([0-9]+))? The last part of the pattern is everything else, starting with a slash. This matches the /index.html part of the URL: /.* Use subpatterns to parse strings.
To make this pattern really useful, we delimit several subpatterns with parentheses: ([^:]+)://([^:/]+)(:([0-9]+))?(/.*) These parentheses do not change the way the pattern matches. Only the optional port number really needs the parentheses in this example. However, the regexp command gives us access to the strings that match these subpatterns. In one step regexp can test for a valid URL and divide it into the protocol part, the server, the port, and the trailing path. The parentheses around the port number include the : before the digits. We've used a dummy variable that gets the : and the port number, and another match variable that just gets the port number. By using noncapturing parentheses in advanced regular expressions, we can eliminate the unused match variable. We can also replace both complementary character sets with a nongreedy .+? match. Example 11-4 shows this variation: Example 11-4 An advanced regular expression to match URLs. set url http://www.beedub.com:80/book/ regexp {(.+?)://(.+?)(?::([0-9]+))?(/.*)}$url \ match protocol server port path => 1 set match => http://www.beedub.com:80/book/ set protocol => http set server
Begins with y or Y, as in a Yes answer. Exactly "yes", "Yes", or "YES". Begins with colon-delimited field that has no spaces or tabs. Same as above, using \S for "not space". A string of all spaces or tabs. A blank line using newline sensitive mode. A blank line, the hard way. Only letters. Only letters, the Unicode way. Letters, digits, and the underscore. Letters, digits, and the underscore using \w. The set of Tcl special characters: ] [ $ { } \ Everything up to a newline. Everything up to a newline using nongreedy *? A period. The set of regular expression special characters: ] [ $ ^ ? + * ( ) | \ An H1 HTML tag. The subpattern matches the string between the tags. HTML comments. 2 hex digits. 2 hex digits, using advanced regular expressions.
{\d{1,3}}
Top
Table of Contents
is replaced. The -nocase, -expanded, -line, -linestop, and -lineanchor switches are the same as in the regexp command. They are described on page 148. The -- switch separates the pattern from the switches, which is necessary if your pattern begins with a -. The replacement pattern, subspec, can contain literal characters as well as the following special sequences:
&
\x , where x is a number, is replaced with the string that matched the corresponding subpattern pattern . The correspondence is based on the order of left parentheses in the pattern
regsub ^$env(HOME)/ $pathname ~/ newpath The following constructs a C compile command line given a filename: set file tclIO.c regsub {([^\.]*)\.c$}$file {cc -c & -o \1.o} ccCmd The matching pattern captures everything before the trailing .c in the file name. The & is replaced with the complete match, tclIO.c, and \1 is replaced with tclIO, which matches the pattern between the parentheses. The value assigned to ccCmd is: cc -c tclIO.c -o tclIO.o We could execute that with: eval exec $ccCmd The following replaces sequences of multiple space characters with a single space: regsub -all {\s+}$string " " string It is perfectly safe to specify the same variable as the input value and the result. Even if there is no match on the pattern, the input string is copied into the output variable. The regsub command can count things for us. The following command counts the newlines in some text. In this case the substitution is not important: set numLines [regsub -all \n $text {} ignore]
Top
Table of Contents
URL Decoding
When a URL is transmitted over the network, it is encoded by replacing special characters with a %xx sequence, where xx is the hexadecimal code for the character. In addition, spaces are replaced with a plus (+). It would be tedious and very inefficient to scan a URL one character at a time with Tcl statements to undo this encoding. It would be more efficient to do this with a custom C program, but still very tedious. Instead, a combination of regsub and subst can efficiently decode the URL in just a few Tcl commands. Replacing the + with spaces requires quoting the + because it is the one-or-more special character in regular expressions: regsub -all {\+}$url {} url The %xx are replaced with a format command that will generate the right character: regsub -all {%([0-9a-hA-H][0-9a-hA-H])} $url \ {[format %c 0x\1]} url The %c directive to format tells it to generate the character from a character code number. We force a hexadecimal interpretation with a leading 0x. Advanced regular expressions let us write the "2 hex digits" pattern a bit more cleanly:
regsub -all {%([[:xdigit:]]{2})} $url \ {[format %c 0x\1]} url The resulting string is passed to subst to get the format commands substituted: set url [subst $url] For example, if the input is %7ewelch, the result of the regsub is: [format %c 0x7e]welch And then subst generates: ~welch Example 11-5 encapsulates this trick in the Url_Decode procedure. Example 11-5 The Url_Decode procedure. proc Url_Decode {url} { regsub -all {\+} $url {} url regsub -all {%([:xdigit:]]{2})} $url \ {[format %c 0x\1]} url return [subst $url] }
Example 11-6 The Cgi_Parse and Cgi_Value procedures. proc Cgi_List {} { set query [Cgi_Query] regsub -all {\+}$query {} query set result {} foreach {x}[split $query &=] { lappend result [Url_Decode $x] } return $result } proc Cgi_Query {} { global env if {![info exists env(QUERY_STRING)] || [string length $env(QUERY_STRING)] == 0} { if {[info exists env(CONTENT_LENGTH)] && [string length $env(CONTENT_LENGTH)] != 0} { set query [read stdin $env(CONTENT_LENGTH)] } else { gets stdin query } set env(QUERY_STRING) $query set env(CONTENT_LENGTH) 0 } return $env(QUERY_STRING) } An HTML form can have several form elements with the same name, and this can result in more than one value for each name. If you blindly use array set to map the results of Cgi_List into an array, you will lose the repeated values. Example 11-6 shows Cgi_Parse and Cgi_Value that store the query data in a global cgi array. Cgi_Parse adds list structure whenever it finds a repeated form value. The global cgilist array keeps a record of how many times a form value is repeated. The Cgi_Value procedure returns elements of the global cgi array, or the empty string if the requested value is not present. Example 11-7 Cgi_Parse and Cgi_Value store query data in the cgi array. proc Cgi_Parse {} { global cgi cgilist catch {unset cgi cgilist} set query [Cgi_Query] regsub -all {\+}$query {}query foreach {name value}[split $query &=] { set name [CgiDecode $name] if {[info exists cgilist($name)] && ($cgilist($name) == 1)} { # Add second value and create list structure
set cgi($name) [list $cgi($name) \ [Url_Decode $value]] } elseif {[info exists cgi($name)]} { # Add additional list elements lappend cgi($name) [CgiDecode $value] } else { # Add first value without list structure set cgi($name) [CgiDecode $value] set cgilist($name) 0 ;# May need to listify } incr cgilist($name) } return [array names cgi] } proc Cgi_Value {key} { global cgi if {[info exists cgi($key)]} { return $cgi($key) } else { return {} } } proc Cgi_Length {key} { global cgilist if {[info exist cgilist($key)]} { return $cgilist($key) } else { return 0 } }
regsub -all {[][$\\]} $text {\\&} new The decimal encoding (e.g., ©) is also more awkward than the hexadecimal encoding used in URLs. We cannot force a decimal interpretation of a number in Tcl. In particular, if the entity has a leading zero (e.g., 
) then Tcl interprets the value (e.g., 010) as octal. The scan command is used to do a decimal interpretation. It scans into a temporary variable, and set is used to get that value: regsub -all {&#([0-9][0-9]?[0-9]?);?} $new \ {[format %c [scan \1 %d tmp; set tmp]]} new With advanced regular expressions, this could be written as follows using bound quantifiers to specify one to three digits: regsub -all {&#(\d{1,3});?} $new \ {[format %c [scan \1 %d tmp;set tmp]]} new The named entities are converted with an array that maps from the entity names to the special character. The only detail is that unknown entity names (e.g., &foobar;) are not converted. This mapping is done inside HtmlMapEntity, which guards against invalid entities. regsub -all {&([a-zA-Z]+)(;?)} $new \ {[HtmlMapEntity \1 \\\2 ]} new If the input text contained: [x < y] then the regsub would transform this into: \[x [HtmlMapEntity lt \; ] y\] Finally, subst will result in: [x < y]
proc Html_DecodeEntity {text} { if {![regexp & $text]} {return $text} regsub -all {[][$\\]}$text {\\&} new regsub -all {&#([0-9][0-9]?[0-9]?);?} $new {\ [format %c [scan \1 %d tmp;set tmp]]} new regsub -all {&([a-zA-Z]+)(;?)} $new \ {[HtmlMapEntity \1 \\\2 ]} new return [subst $new] } proc HtmlMapEntity {text {semi {}}} { global htmlEntityMap if {[info exist htmlEntityMap($text)]} { return $htmlEntityMap($text) } else { return $text$semi } } # Some of the htmlEntityMap array set htmlEntityMap { lt < gt > amp & aring \xe5 atilde \xe3 copy \xa9 ecirc \xea egrave \xe8 }
# \3 is the parameters to the tag, if any # The curly braces at either end group of all the text # after the HTML tag, which becomes the last arg to $cmd. set sub "\}\n {\\2} {\\1} {\\3} \{" regsub -all $exp $html $sub html # This balances the curly braces, # and calls $cmd with $start as a pseudo-tag # at the beginning and end of the script. eval "$cmd {$start} {} {} {$html}" eval "$cmd {$start} / {} {}" } The main regsub pattern can be written more simply with advanced regular expressions: set exp {<(/?)(\S+?)\s*(.*?)>} An example will help visualize the transformation. Given this HTML: <Title>My Home Page</Title> <Body bgcolor=white text=black> <H1>My Home</H1> This is my <b>home</b> page. and a call to Html_Parse that looks like this: Html_Parse $html {Render .text}hmstart then the generated program is this: Render .text Render .text Render .text } Render .text } Render .text Render .text This is my } Render .text Render .text } Render .text {hmstart} {} {} {} {Title} {} {} {My Home Page} {Title} {/} {} { {Body} {} {bgcolor=white text=black} { {H1} {} {} {My Home} {H1} {/} {} { {b} {} {} {home} {b} {/} {} {page. {hmstart}/ {} {}
One overall point to make about this example is the difference between using eval and subst with the generated script. The decoders shown in Examples 11-5 and 11-8 use subst to selectively replace encoded characters while ignoring the rest of the text. In Html_Parse we must process all the text. The main trick is to replace the matching text (e.g., the HTML tag) with some Tcl code that ends in an open curly brace and starts with a close curly brace. This effectively groups all the unmatched text. When eval is used this way you must do something with any braces and backslashes in the unmatched text. Otherwise, the resulting script does not parse correctly. In this case, these special characters are encoded as HTML entities. We can afford to do this because the cmd that is called must deal with encoded entities already. It is not possible to quote these special characters with backslashes because all this text is inside curly braces, so no backslash substitution is performed. If you try that the backslashes will be seen by the cmd callback. Finally, I must admit that I am always surprised that this works: eval "$cmd {$start} {} {} {$html}" I always forget that $start and $html are substituted in spite of the braces. This is because double quotes are being used to group the argument, so the quoting effect of braces is turned off. Try this: set x hmstart set y "foo {$x}bar" => foo {hmstart}bar
regsub -all --> $html \x81 html This replaces all the end comment sequences with a single character that is not allowed in HTML. Now you can delete the comments like this: regsub -all "<!--\[^\x81\]*\x81" $html {}html
Top
Table of Contents
takes a -regexp flag, so you can branch based on a regular expression match instead of an exact match or a string match style match. The switch command is described on page 71.
switch
The Tk text widget can search its contents based on a regular expression match. Searching in the text widget is described on page 463. The Expect Tcl extension can match the output of a program with regular expressions. Expect is the subject of its own book, Exploring Expect (O'Reilly, 1995) by Don Libes.
Top
Table of Contents
Top
Table of Contents
Packages in this location, for example, will be found automatically because the auto_path list includes /usr/local/tcl/lib. You can also add directories to the auto_path explicitly: lappend auto_path directory One trick I often use is to put the directory containing the main script into the auto_path. The following command sets this up: lappend auto_path [file dirname [info script]] If your code is split into bin and lib directories, then scripts in the bin directory can add the adjacent lib directory to their auto_path with this command: lappend auto_path \ [file join [file dirname [info script]] ../lib]
Top
Table of Contents
Using Packages
Each script file in a library declares what package it implements with the package provide command: package provide name version The name identifies the package, and the version has a major.minor format. The convention is that the minor version number can change and the package implementation will still be compatible. If the package changes in an incompatible way, then the major version number should change. For example, Chapter 17 defines several procedures that use the HTTP network protocol. These include Http_Open, Http_Get, and Http_Validate. The file that contains the procedures starts with this command: package provide Http 1.0 Case is significant in package names. In particular, the package that comes with Tcl is named http ?all lowercase. More than one file can contribute to the same package simply by specifying the same name and version . In addition, different versions of the same package can be kept in the same directory but in different files. An application specifies the packages it needs with the package require command: package require name ?version? ?-exact? If the version is left off, then the highest available version is loaded. Otherwise the highest version with the same major number is loaded. For example, if the client requires version 1.1, version 1.2 could be loaded if it exists, but versions 1.0 and 2.0 would not be loaded. You can restrict the package to a specific version with the -exact flag. If no matching version can be found, then the package require command raises an error.
Generates an index with source and load commands in it. This results in packages being loaded directly as a result of package require. Dynamically loads packages that match pattern into the slave interpreter used to compute the index. A common reason to need this is with the tcbload package needed to load .tbc files compiled with TclPro Compiler. Displays the name of each file processed and any errors that occur.
610. Chapter 37 also describes the Tcl load command that is used instead of source to link in shared libraries. The pkg_mkIndex command also handles shared libraries: pkg_mkIndex directory *.tcl *.so *.shlib *.dll In this example, .so, .shlib, and .dll are file suffixes for shared libraries on UNIX, Macintosh, and Windows systems, respectively. You can have packages that have some of their commands implemented in C, and some implemented as Tcl procedures. The script files and the shared library must simply declare that they implement the same package. The pkg_mkIndex procedure will detect this and set up the auto_index, so some commands are defined by sourcing scripts, and some are defined by loading shared libraries. If your file servers support more than one machine architecture, such as Solaris and Linux systems, you probably keep the shared library files in machine-specific directories. In this case the auto_path should also list the machine-specific directory so that the shared libraries there can be loaded automatically. If your system administrator configured the Tcl installation properly, this should already be set up. If not, or you have your shared libraries in a nonstandard place, you must append the location to the auto_path variable.
Top
Table of Contents
Use pkg_mkIndex to maintain your index files. Decide at this time whether or not to use direct package loading.
Put the appropriate package require and package provide commands in your code. Ensure that your library directories, or their parent directories, are listed in the auto_path variable.
Top
Table of Contents
Deletes registration information for package. Queries or sets the command used to set up automatic loading of a package. Returns the set of registered packages. Declares that a script file defines commands for package with the given version. Declares that a script uses package. The -exact flag specifies that the exact version must be loaded. Otherwise, the highest matching version is loaded. Queries or sets the command used to locate packages. Compares version v1 and v2. Returns 0 if they are equal, minus 1 if v1 is less than v2, or 1 if v1 is greater than v2. Returns which versions of the package are registered. Returns 1 if v1 is greater or equal to v2 and still has the same major version number. Otherwise returns 0.
Top
Table of Contents
set doit 1 } else { # Check each file for modification foreach file [glob [file join $libdir *.tcl]] { if {[file mtime $file] > $age} { set doit 1 break } } } } if { $doit } { auto_mkindex $libdir *.tcl } } Tcl uses the auto_path variable to record a list of directories to search for unknown commands. To continue our example, you can make the procedures in the book examples available by putting this command at the beginning of your scripts: lappend auto_path /usr/local/tcl/welchbook This has no effect if you have not created the tclIndex file. If you want to be extra careful, you can call Library_UpdateIndex. This will update the index if you add new things to the library. lappend auto_path /usr/local/tcl/welchbook Library_UpdateIndex /usr/local/tcl/welchbook This will not work if there is no tclIndex file at all because Tcl won't be able to find the implementation of Library_UpdateIndex. Once the tclIndex has been created for the first time, then this will ensure that any new procedures added to the library will be installed into tclIndex. In practice, if you want this sort of automatic update, it is wise to include something like the Library_UpdateIndex procedure directly into your application as opposed to loading it from the library it is supposed to be maintaining.
Top
Table of Contents
# No index continue } # eval the file as a script. Because eval is # used instead of source, an extra round of # substitutions is performed and $dir gets expanded # The real code checks for errors here. eval [read $f] close $f }
Top
Table of Contents
Interactive Conveniences
The unknown command provides a few other conveniences. These are used only when you are typing commands directly. They are disabled once execution enters a procedure or if the Tcl shell is not being used interactively. The convenience features are automatic execution of programs, command history, and command abbreviation. These options are tried, in order, if a command implementation cannot be loaded from a script library.
Auto Execute
The unknown procedure implements a second feature: automatic execution of external programs. This makes a Tcl shell behave more like other UNIX shells that are used to execute programs. The search for external programs is done using the standard PATH environment variable that is used by other shells to find programs. If you want to disable the feature all together, set the auto_noexec variable: set auto_noexec anything
History
The history facility described in Chapter 13 is implemented by the unknown procedure.
Abbreviations
If you type a unique prefix of a command, unknown recognizes it and executes the matching command for you. This is done after automatic program execution is attempted and history substitutions are performed.
Top
Table of Contents
The first two directories correspond to the standard installation directories, while the last three correspond to the standard build environment for Tcl or Tk. The first directory in the list that contains a valid init.tcl file becomes the Tcl script library. This directory location is saved in the tcl_library global variable, and it is also returned by the info library command. The primary thing defined by init.tcl is the implementation of the unknown procedure. It also initializes auto_path to contain $tcl_library and the parent directory of $tcl_library. There may be additional directories added to auto_path depending on the compiled in value of tcl_pkgPath. tcl_findLibrary A generalization of this search is implemented by tcl_findLibrary. This procedure is designed for use by extensions like Tk and [incr Tcl]. Of course, Tcl cannot use tcl_findLibrary itself because it is defined in init.tcl! The tcl_findLibrary procedure searches relative to the location of the main program (e.g., tclsh or wish) and assumes a standard installation or a standard build environment. It also supports an override by an environment variable, and it takes care of sourcing an initialization script. The usage of tcl_findLibrary is: tcl_findLibrary base version patch script enVar varName The base is the prefix of the script library directory name. The version is the main version number (e.g., "8.0"). The patch is the full patch level (e.g., "8.0.3"). The script is the initialization script to source from the directory. The enVar names an environment variable that can be used to override the default search path. The varName is the name of a variable to set to name of the directory found by tcl_findLibrary. A side effect of tcl_findLibrary is to source the script from the directory. An example call is: tcl_findLibrary tk 8.0 8.0.3 tk.tcl TK_LIBRARY tk_library This call first checks to see whether TK_LIBRARY is defined in the environment. If so, it uses its value. Otherwise, it searches the following directories for a file named tk.tcl. It sources the script and sets the tk_library variable to the directory containing that file. The search is relative to the value returned by info nameofexecutable: ../lib/tk8.0 ../../lib/tk8.0 ../library ../../tk8.0.3/library ../../../tk8.0.3/library Tk also adds $tk_library to the end of auto_path, so the other script files in that directory are
Top
Table of Contents
Coding Style
If you supply a package, you need to follow some simple coding conventions to make your library easier to use by other programmers. You can use the namespace facility introduced in Tcl 8.0. You can also use conventions to avoid name conflicts with other library packages and the main application. This section describes the conventions I developed before namespaces were added to Tcl.
In general, I try to use a single global array for a package. The array provides a convenient place to collect a set of related variables, much as a struct is used in C. For example, the preferences package uses the pref array to hold all its state information. It is also a good idea to keep the use of the array private. It is better coding practice to provide exported procedures than to let other modules access your data structures directly. This makes it easier to change the implementation of your package without affecting its clients. If you do need to export a few key variables from your module, use the underscore convention to distinguish exported variables. If you need more than one global variable, just stick with the prefix convention to avoid conflicts.
Top
Table of Contents
Top
Table of Contents
A system-dependent high resolution counter. Formats a clock value according to str. Parses date string and return seconds value. The clock value determines the date. Returns the current time in seconds.
The following command prints the current time: clock format [clock seconds] => Sun Nov 24 14:57:04 1996 The clock seconds command returns the current time, in seconds since a starting epoch. The clock format command formats an integer value into a date string. It takes an optional argument that controls the format. The format strings contains % keywords that are replaced with the year, month, day, date, hours, minutes, and seconds, in various formats. The default string is: %a %b %d %H:%M:%S %Z %Y Tables 13-2 and 13-3 summarize the clock formatting strings:
Inserts a %. Abbreviated weekday name (Mon, Tue, etc.). Full weekday name (Monday, Tuesday, etc.). Abbreviated month name (Jan, Feb, etc.). Full month name. Locale specific date and time (e.g., Nov 24 16:00:59 1996). Day of month (01 ?31). Hour in 24-hour format (00 ?23). Hour in 12-hour format (01 ?12). Day of year (001 ?366). Month number (01 ?12). Minute (00 ?59). AM/PM indicator. Seconds (00 ?59). Week of year (00 ?52) when Sunday starts the week. Weekday number (Sunday = 0). Week of year (01 ?52) when Monday starts the week. Locale specific date format (e.g., Feb 19 1997). Locale specific time format (e.g., 20:10:13). Year without century (00 ?99). Year with century (e.g. 1997). Time zone name. Table 13-3. UNIX-specific clock formatting keywords.
%D %e %h %n %r %R %t %T
Date as %m/%d/%y (e.g., 02/19/97). Day of month (1 ?31), no leading zeros. Abbreviated month name. Inserts a newline. Time as %I:%M:%S %p (e.g., 02:39:29 PM). Time as %H:%M (e.g., 14:39). Inserts a tab. Time as %H:%M:%S (e.g., 14:34:29).
The clock clicks command returns the value of the system's highest resolution clock. The units of the clicks are not defined. The main use of this command is to measure the relative time of different performance tuning trials. The following command counts the clicks per second over 10 seconds, which will vary from system to system: Example 13-1 Calculating clicks per second. set t1 [clock clicks] after 10000 ;# See page 218 set t2 [clock clicks] puts "[expr ($t2 - $t1)/10] Clicks/second" => 1001313 Clicks/second The clock scan command parses a date string and returns a seconds value. The command handles a variety of date formats. If you leave off the year, the current year is assumed. Year 2000 Compliance
Tcl implements the standard interpretation of two-digit year values, which is that 70?9 are 1970?999, 00?9 are 2000?069. Versions of Tcl before 8.0 did not properly deal with two-digit years in all cases. Note, however, that Tcl is limited by your system's time epoch and the number of bits in an integer. On Windows, Macintosh, and most UNIX systems, the clock epoch is January 1, 1970. A 32-bit integer can count enough seconds to reach forward into the year 2037, and backward to the year 1903. If you try to clock scan a date outside that range, Tcl will raise an error because the seconds counter will overflow or underflow. In this case, Tcl is just reflecting limitations of the underlying system. If you leave out a date, clock scan assumes the current date. You can also use the -base option to specify a date. The following example uses the current time as the base, which is redundant:
clock scan "10:30:44 PM" -base [clock seconds] => 2931690644 The date parser allows these modifiers: year, month, fortnight (two weeks), week, day, hour, minute, second. You can put a positive or negative number in front of a modifier as a multiplier. For example: clock format [clock scan "10:30:44 PM 1 week"] => Sun Dec 01 22:30:44 1996 clock format [clock scan "10:30:44 PM -1 week"] Sun Nov 17 22:30:44 1996 You can also use tomorrow, yesterday, today, now, last, this, next, and ago, as modifiers. clock format [clock scan "3 years ago"] => Wed Nov 24 17:06:46 1993 Both clock format and clock scan take a -gmt option that uses Greenwich Mean Time. Otherwise, the local time zone is used. clock format [clock seconds] -gmt true => Sun Nov 24 09:25:29 1996 clock format [clock seconds] -gmt false => Sun Nov 24 17:25:34 1996
Top
Table of Contents
A list of procedure's arguments. The commands in the body of procedure. The number of commands executed so far. A list of all commands, or those matching pattern. Includes built-ins and Tcl procedures. True if string contains a complete Tcl command. True if arg has a default parameter value in procedure proc. The default value is stored into var. True if variable is defined. A list of all global variables, or those matching pattern. The name of the machine. This may be the empty string if networking is not initialized. The stack level of the current procedure, or 0 for the global scope. A list of the command and its arguments at the specified level of the stack. The pathname of the Tcl library directory. A list of the libraries loaded into the interpreter named interp, which defaults to the current one. A list of all local variables, or those matching pattern.
info nameofexecutable info patchlevel info procs ?pattern? info script info sharedlibextension info tclversion info vars ?pattern?
The file name of the program (e.g., of tclsh or wish). The release patch level for Tcl. A list of all Tcl procedures, or those that match pattern. The name of the file being processed, or the empty string. The file name suffix of shared libraries. The version number of Tcl. A list of all visible variables, or those matching pattern.
Variables
There are three categories of variables: local, global, and visible. Information about these categories is returned by the locals, globals, and vars operations, respectively. The local variables include procedure arguments as well as locally defined variables. The global variables include all variables defined at the global scope. The visible variables include locals, plus any variables made visible via global or upvar commands. A pattern can be specified to limit the returned list of variables to those that match the pattern. The pattern is interpreted according to the rules of string match, which is described on page 48: info globals auto* => auto_index auto_noexec auto_path Namespaces, which are the topic of the next chapter, partition global variables into different scopes. You query the variables visible in a namespace with: info vars namespace::* Remember that a variable may not be defined yet even though a global or upvar command has declared it visible in the current scope. Use the info exists command to test whether a variable or an array element is defined or not. An example is shown on page 90.
Procedures
You can find out everything about a Tcl procedure with the args, body, and default operations. This is illustrated in the following Proc_Show example. The puts commands use the -nonewline flag because the newlines in the procedure body, if any, are retained: Example 13-2 Printing a procedure definition. proc Proc_Show {{namepat *}{file stdout}}{ foreach proc [info procs $namepat] {
set space "" puts -nonewline $file "proc $proc {" foreach arg [info args $proc] { if [info default $proc $arg value] { puts -nonewline $file "$space{$arg $value}" } else { puts -nonewline $file $space$arg } set space " " } # No newline needed because info body may return a # value that starts with a newline puts -nonewline $file "}{" puts -nonewline $file [info body $proc] puts $file "}" } } Example 13-3 is a more elaborate example of procedure introspection that comes from the direct.tcl file, which is part of the Tcl Web Server described in Chapter 18. This code is used to map URL requests and the associated query data directly into Tcl procedure calls. This is discussed in more detail on page 247. The Web server collects Web form data into an array called form. Example 13-3 matches up elements of the form array with procedure arguments, and it collects extra elements into an args parameter. If a form value is missing, then the default argument value or the empty string is used: Example 13-3 Mapping form data onto procedure arguments. # cmd is the name of the procedure to invoke # form is an array containing form values set cmdOrig $cmd set params [info args $cmdOrig] # Match elements of the form array to parameters foreach arg $params { if {![info exists form($arg)]} { if {[info default $cmdOrig $arg value]} { lappend cmd $value } elseif {[string compare $arg "args"] == 0} { set needargs yes } else { lappend cmd {} } } else { lappend cmd $form($arg)
} } # If args is a parameter, then append the form data # that does not match other parameters as extra parameters if {[info exists needargs]} { foreach {name value} $valuelist { if {[lsearch $params $name] < 0} { lappend cmd $name $value } } } # Eval the command set code [catch $cmd result] The info commands operation returns a list of all commands, which includes both built-in commands defined in C and Tcl procedures. There is no operation that just returns the list of built-in commands. Example 13-4 finds the built-in commands by removing all the procedures from the list of commands. Example 13-4 Finding built-in commands. proc Command_Info {{pattern *}}{ # Create a table of procedures for quick lookup foreach p [info procs $pattern] { set isproc($p) 1 } # Look for command not in the procedure table set result {} foreach c [info commands $pattern] { if {![info exists isproc($c)]}{ lappend result $c } } return [lsort $result] }
name and argument values at that level in the call stack. If a negative level is specified, then it is relative to the current call stack. Relative level -1 is the level of the current procedure's caller, and relative level 0 is the current procedure. The following example prints the call stack. The Call_trace procedure avoids printing information about itself by starting at one less than the current call stack level: Example 13-5 Getting a trace of the Tcl call stack. proc Call_Trace {{file stdout}}{ puts $file "Tcl Call Trace" for {set x [expr [info level]-1]}{$x > 0}{incr x -1}{ puts $file "$x: [info level $x]" } }
Command Evaluation
If you want to know how many Tcl commands are executed, use the info cmdcount command. This counts all commands, not just top-level commands. The counter is never reset, so you need to sample it before and after a test run if you want to know how many commands are executed during a test. The info complete operation figures out whether a string is a complete Tcl command. This is useful for command interpreters that need to wait until the user has typed in a complete Tcl command before passing it to eval. Example 13-6 defines Command_Process that gets a line of input and builds up a command. When the command is complete, the command is executed at the global scope. Command_Process takes two callbacks as arguments. The inCmd is evaluated to get the line of input, and the outCmd is evaluated to display the results. Chapter 10 describes callbacks why the curly braces are used with eval as they are in this example: Example 13-6 A procedure to read and evaluate commands. proc Command_Process {inCmd outCmd}{ global command append command(line) [eval $inCmd] if [info complete $command(line)] { set code [catch {uplevel #0 $command(line)}result] eval $outCmd {$result $code} set command(line) {} } } proc Command_Read {{in stdin}}{ if [eof $in] { if {$in != "stdin"}{ close $in } return {} }
return [gets $in] } proc Command_Display {file result code}{ puts stdout $result } while {![eof stdin]}{ Command_Process {Command_Read stdin}\ {Command_Display stdout} }
I often use info script to source or process files stored in the same directory as the script that is running. A few examples are shown in Example 13-7. Example 13-7 Using info script to find related files. # Get the directory containing the current script. set dir [file dirname [info script]] # Source a file in the same directory source [file join $dir helper.tcl] # Add an adjacent script library directory to auto_path # The use of ../lib with file join is cross-platform safe. lappend auto_path [file join $dir ../lib] The pathname of the Tcl library is stored in the tcl_library variable, and it is also returned by the info library command. While you could put scripts into this directory, it might be better to have a separate directory and use the script library facility described in Chapter 12. This makes it easier to deal with new releases of Tcl and to package up your code if you want other sites to use it.
Version Numbers
Each Tcl release has a version number such as 7.4 or 8.0. This number is returned by the info tclversion command. If you want your script to run on a variety of Tcl releases, you may need to test the version number and take different actions in the case of incompatibilities between releases. The Tcl release cycle starts with one or two alpha and beta releases before the final release, and there may even be a patch release after that. The info patchlevel command returns a qualified version number, like 8.0b1 for the first beta release of 8.0. We switched from using "p" (e.g., 8.0p2) to a threelevel scheme (e.g., 8.0.3) for patch releases. The patch level is zero for the final release (e.g., 8.2.0). In general, you should be prepared for feature changes during the beta cycle, but there should only be bug fixes in the patch releases. Another rule of thumb is that the Tcl script interface remains quite compatible between releases; feature additions are upward compatible.
Execution Environment
The file name of the program being executed is returned with info nameofexecutable. This is more precise than the name in the argv0 variable, which could be a relative name or a name found in a command directory on your command search path. It is still possible for info nameofexecutable to return a relative pathname if the user runs your program as ./foo, for example. The following construct always returns the absolute pathname of the current program. If info nameofexecutable returns an absolute pathname, then the value of the current directory is ignored. The pwd command is described on page 115: file join [pwd] [info nameofexecutable] A few operations support dynamic loading of shared libraries, which are described in Chapter 44. The info sharedlibextension returns the file name suffix of dynamic link libraries. The info loaded command returns a list of libraries that have been loaded into an interpreter. Multiple interpreters are described in Chapter 19.
Top
Table of Contents
Cross-Platform Support
Tcl is designed so that you can write scripts that run unchanged on UNIX, Macintosh, and Windows platforms. In practice, you may need a small amount of code that is specific to a particular platform. You can find out information about the platform via the tcl_platform variable. This is an array with these elements defined:
tcl_platform(platform)
tcl_platform(os) identifies the operating system. Examples include MacOS, Solaris , Linux, Win32s (Windows 3.1 with the Win32 subsystem), Windows 95, Windows NT, and SunOS. tcl_platform(osVersion) gives the version number of the operating system. tcl_platform(machine) identifies the hardware. Examples include ppc (Power (68000 family), sparc, intel, mips, and alpha. tcl_platform(isWrapped) indicates
PC), 68k
that the application has been wrapped up into a single executable with TclPro Wrapper. This is not defined in normal circumstances.
tcl_platform(user) gives the login name of the current user. tcl_platform(debug) indicates that Tcl was compiled with debugging symbols. tcl_platform(thread) indicates that Tcl was compiled with thread support enabled.
On some platforms a hostname is defined. If available, it is returned with the info hostname command. This command may return an empty string. One of the most significant areas affected by cross-platform portability is the file system and the way files are named. This topic is discussed on page 103.
Top
Table of Contents
Read-Only Variables
Example 13-8 uses traces to implement a read-only variable. A variable is modified before the trace procedure is called, so the ReadOnly variable is needed to preserve the original value. When a variable is unset, the traces are automatically removed, so the unset trace action reestablishes the trace explicitly. Note that the upvar alias (e.g., var) cannot be used to set up the trace:
Example 13-8 Tracing variables. proc ReadOnlyVar {varName}{ upvar 1 $varName var global ReadOnly set ReadOnly($varName) $var trace variable $varName wu ReadOnlyTrace } proc ReadOnlyTrace { varName index op }{ global ReadOnly upvar 1 $varName var switch $op { w { set var $ReadOnly($varName) } u { set var $ReadOnly($varName) # Re-establish the trace using the true name trace variable $varName wu ReadOnlyTrace } } } This example merely overrides the new value with the saved value. Another alternative is to raise an error with the error command. This will cause the command that modified the variable to return the error. Another common use of trace is to update a user interface widget in response to a variable change. Several of the Tk widgets have this feature built into them. If more than one trace is set on a variable, then they are invoked in the reverse order; the most recent trace is executed first. If there is a trace on an array and on an array element, then the trace on the array is invoked first.
Information about traces on a variable is returned with the vinfo option: trace vinfo dynamic => {r FixupDynamic} A trace is deleted with the vdelete option, which has the same form as the variable option. The trace in the previous example can be removed with the following command: trace vdelete dynamic r FixupDynamic
Top
Table of Contents
history history add command ? exec? history change new ? event? history event ?event? history info ?count? history keep count history nextid history redo ?event?
Short for history info with no count. Adds the command to the history list. If exec is specified, then execute the command. Changes the command specified by event to new in the command history. Returns the command specified by event. Returns a formatted history list of the last count commands, or of all commands. Limits the history to the last count commands. Returns the number of the next event. Repeats the specified command.
History Syntax
Some extra syntax is supported when running interactively to make the history facility more convenient to use. Table 13-6 shows the special history syntax supported by tclsh and wish.
Repeats the previous command. Repeats command number n.If n is negative it counts backward from the current command. The previous command is event -1. Repeats the last command that begins with prefix. Repeats the last command that matches pattern. Globally replaces old with new in the last command.
The next example shows how some of the history operations work: Example 13-10 Interactive history usage. % set a 5 5 % set a [expr $a+7] 12 % history 1 set a 5 2 set a [expr $a+7] 3 history % !2 19
% !! 26 % ^7^13 39 % !h 1 set a 5 2 set a [expr 3 history 4 set a [expr 5 set a [expr 6 set a [expr 7 history
Top
Table of Contents
Debugging
The rapid turnaround with Tcl coding means that it is often sufficient to add a few puts statements to your script to gain some insight about its behavior. This solution doesn't scale too well, however. A slight improvement is to add a Debug procedure that can have its output controlled better. You can log the information to a file, or turn it off completely. In a Tk application, it is simple to create a text widget to hold the contents of the log so that you can view it from the application. Here is a simple Debug procedure. To enable it you need to set the debug(enable) variable. To have its output go to your terminal, set debug(file) to stderr. Example 13-12 A Debug procedure. proc Debug { args }{ global debug if {![info exists debug(enabled)]}{ # Default is to do nothing return } puts $debug(file) [join $args " "] } proc DebugOn {{file {}}}{ global debug set debug(enabled) 1 if {[string length $file] == 0}{ set debug(file) stderr } else { if [catch {open $file w}fileID] { puts stderr "Cannot open $file: $fileID" set debug(file) stderr } else { puts stderr "Debug info to $file" set debug(file) $fileID } } }
proc DebugOff {}{ global debug if {[info exists debug(enabled)]}{ unset debug(enabled) flush $debug(file) if {$debug(file) != "stderr" && $debug(file) != "stdout"}{ close $debug(file) unset debug(file) } } }
Top
Table of Contents
Scriptics' TclPro
Scriptics offers a commercial development environment for Tcl called TclPro. TclPro features an extended Tcl platform and a set of development tools. The Tcl platform includes the popular [incr Tcl], Expect, and TclX extensions. These extensions and Tcl/Tk are distributed in source and binary form for Windows and a variety of UNIX platforms. There is an evaluation copy of TclPro on the CDROM. The TclPro distribution includes a copy of Tcl/Tk and the extensions that you can use for free. However, you will need to register at the Scriptics web site to obtain an evaluation license for the TclPro development tools. Please visit the following URL: http://www.scriptics.com/registration/welchbook.html The current version of TclPro contains these tools:
TclPro Debugger
TclPro Debugger provides a nice graphical user interface with all the features you expect from a traditional debugger. You can set breakpoints, single step, examine variables, and look at the call stack. It understands a subtle issue that can arise from using the update command: nested call stacks. It is possible to launch a new Tcl script as a side effect of the update command, which pushes the current state onto the execution stack. This shows up clearly in the debugger stack trace. It maintains project state, so it will remember breakpoint settings and other preference items between runs. One of the most interesting features is that it can debug remotely running applications. I use it regularly to debug Tcl code running inside the Tcl Web Server.
TclPro Checker
TclPro Checker is a static code checker. This is a real win for large program development. It examines every line of your program looking for syntax errors and dubious coding practices. It has detailed knowledge of Tcl, Tk, Expect, [incr Tcl], and TclX commands and validates your use of them. It checks that you call Tcl procedures with the correct number of arguments, and can cross-check large groups of Tcl files. It knows about changes between Tcl versions, and it can warn you about old code that needs to be updated.
TclPro Compiler
TclPro Compiler is really just a reader and writer for the byte codes that the Tcl byte-code compiler generates internally. It lets you precompile scripts and save the results, and then load the byte-code later instead of raw source. This provides a great way to hide your source code, if that is important to you. It turns out to save less time than you might think, however. By the time it reads the file from disk, decodes it, and builds the necessary Tcl data structures, it is not much faster than reading a source file and compiling it on the fly.
TclPro Wrapper
TclPro Wrapper assembles a collection of Tcl scripts, data files, and a Tcl/Tk interpreter into a single executable file. This makes distribution of your Tcl application as easy as giving out one file. The Tcl C library has been augmented with hooks in its file system access routines so that a wrapped application can look inside itself for files. The rule is that if you use a relative pathname (i.e., lib/myfile.dat), then the wrapped application will look first inside itself for the file. If the file is not found, or if the pathname is absolute (e.g., /usr/local/lib/myfile.dat), then Tcl looks on your hard disk for the file. The nice thing about TclPro Wrapper is that it handles all kinds of files, not just Tcl source files. It works by concatenating a ZIP file onto the end of a specially prepared Tcl interpreter. TclPro comes with pre-built interpreters that include Expect, [incr Tcl], and TclX, or you can build your own interpreter that contains custom C extensions.
Top
Table of Contents
Other Tools
The Tcl community has built many interesting and useful tools to help your Tcl development. Only two of them are mentioned below, but you can find many more at the Scriptics Tcl Resource Center: http://www.scriptics.com/resource/
application gets an error it offers to send mail to me with a few words of explanation from the user and a copy of the stack trace. I get interesting bug reports from all over the world! The bgerror command is called with one argument that is the error message. The global variable errorInfo contains the stack trace information. There is an example tkerror implementation in the on-line sources associated with this book.
Top
Table of Contents
Performance Tuning
The time command measures the execution time of a Tcl command. It takes an optional parameter that is a repetition count: time {set a "Hello, World!"}1000 => 28 microseconds per iteration If you need the result of the command being timed, use set to capture the result: puts $log "command: [time {set result [command]}]"
[expr $now - $log(last)] \ [join $args " "]] set log(last) $now } } proc Log_Open {file}{ global log catch {close $log(file)} set log(file) [open $file w] set log(last) [clock clicks] } proc Log_Flush {}{ global log catch {flush $log(file)} } proc Log_Close {}{ global log catch {close $log(file)} catch {unset log(file)} } A more advanced profile command is part of the Extended Tcl (TclX) package, which is described in Tcl/Tk Tools (Mark Harrison, ed., O'Reilly & Associates, Inc., 1997). The TclX profile command monitors the number of calls, the CPU time, and the elapsed time spent in different procedures.
The previous expression is not fully defined until runtime, so it has to be parsed and executed each time it is used. If the expression is grouped with braces, then the compiler knows in advance what operations will be used and can generate byte codes to implement the expression more efficiently. The operation of the compiler is essentially transparent to scripts, but there are some differences in lists and expressions. These are described in Chapter 51. With lists, the good news is that large lists are more efficient. The problem is that lists are parsed more aggressively, so syntax errors at the end of a list will be detected even if you access only the beginning of the list. There were also some bugs in the code generator in the widely used Tcl 8.0p2 release. Most of these were corner cases like unbraced expressions in if and while commands. Most of these bugs were fixed in the 8.0.3 patch release, and the rest were cleaned up in Tcl 8.1 with the addition of a new internal parsing package.
Top
Table of Contents
Top
Table of Contents
Using Namespaces
Namespaces add new syntax to procedure and variable names. A double colon, ::, separates the namespace name from the variable or procedure name. You use this syntax to reference procedures and variables in a different namespace. The namespace import command lets you name things in other namespaces without the extra syntax. Namespaces can be nested, so you can create a hierarchy of scopes. These concepts are explained in more detail in the rest of this chapter. One feature not provided by namespaces is any sort of protection, or a way to enforce access controls between different namespaces. This sort of thing is awkward, if not impossible, to provide in a dynamic language like Tcl. For example, you are always free to use namespace eval to reach into any other namespace. Instead of providing strict controls, namespaces are meant to provide structure that enables large scale programming. The package facility described in Chapter 12 was designed before namespaces. This chapter illustrates a style that ties the two facilities together, but they are not strictly related. It is possible to create a package named A that implements a namespace B, or to use a package without namespaces, or a namespace without a package. However, it makes sense to use the facilities together. Example 14-1 repeats the random number generator from Example 7-4 on page 85 using namespaces. The standard naming style conventions for namespaces use lowercase: Example 14-1 Random number generator using namespaces. package provide random 1.0 namespace eval random { # Create a variable inside the namespace variable seed [clock seconds] # Make the procedures visible to namespace import namespace export init random range # Create procedures inside the namespace proc init { value } {
variable seed set seed $value } proc random {} { variable seed set seed [expr ($seed*9301 + 49297) % 233280] return [expr $seed/double(233280)] } proc range { range } { expr int([random]*$range) } } Example 14-1 defines three procedures and a variable inside the namespace random. From inside the namespace, you can use these procedures and variables directly. From outside the namespace, you use the :: syntax for namespace qualifiers. For example, the state variable is just seed within the namespace, but you use random::seed to refer to the variable from outside the namespace. Using the procedures looks like this: random::random => 0.3993355624142661 random::range 10 => 4 If you use a package a lot you can import its procedures. A namespace declares what procedures can be imported with the namespace export command. Once you import a procedure, you can use it without a qualified name: namespace import random::random random => 0.54342849794238679 Importing and exporting are described in more detail later.
Top
Table of Contents
Namespace Variables
The variable command defines a variable inside a namespace. It is like the set command because it can define a value for the variable. You can declare several namespace variables with one variable command. The general form is: variable name ?value? ?name value? ... If you have an array, do not assign a value in the variable command. Instead, use regular Tcl commands after you declare the variable. You can put any commands inside a namespace block: namespace eval foo { variable arr array set arr {name value name2 value2} } A namespace variable is similar to a global variable because it is outside the scope of any procedures. Procedures use the variable command or qualified names to reference namespace variables. For example, the random procedure has a variable command that brings the namespace variable into the current scope: variable seed If a procedure has a variable command that names a new variable, it is created in the namespace when it is first set. Watch out for conflicts with global variables.
You need to be careful when you use variables inside a namespace block. If you declare them with a variable command, they are clearly namespace variables. However, if you forget to declare them, then they will either become namespace variables, or latch onto an existing global variable by the same name. Consider the following code: namespace eval foo { variable table for {set i 1} {$i <= 256} {incr i} { set table($i) [format %c $i] } } If there is already a global variable i, then the for loop will use that variable. Otherwise, it will create the foo::i variable. I found this behavior surprising, but it does make it easier to access global variables like env without first declaring them with global inside the namespace block.
Qualified Names
A fully qualified name begins with ::, which is the name for the global namespace. A fully qualified name unambiguously names a procedure or a variable. The fully qualified name works anywhere. If you use a fully qualified variable name, it is not necessary to use a global command. For example, suppose namespace foo has a namespace variable x, and there is also a global variable x. The global variable x can be named with this: ::x The :: syntax does not affect variable substitutions. You can get the value of the global variable x with $::x. Name the namespace variable x with this: ::foo::x A partially qualified name does not have a leading ::. In this case the name is resolved from the current namespace. For example, the following also names the namespace variable x: foo::x You can use qualified names with global. Once you do this, you can access the variable with its short name:
global ::foo::x set x 5 Declaring variables is more efficient than using qualified names.
The Tcl byte-code compiler generates faster code when you declare namespace and global variables. Each procedure context has its own table of variables. The table can be accessed by a direct slot index, or by a hash table lookup of the variable name. The hash table lookup is slower than the direct slot access. When you use the variable or global command, then the compiler can use a direct slot access. If you use qualified names, the compiler uses the more general hash table lookup.
Top
Table of Contents
Command Lookup
A command is looked up first in the current name space. If it is not found there, then it is looked up in the global namespace. This means that you can use all the built-in Tcl commands inside a namespace with no special effort. You can play games by redefining commands within a namespace. For example, a namespace could define a procedure named set. To get the built-in set you could use ::set, while set referred to the set defined inside namespace. Obviously you need to be quite careful when you do this. You can use qualified names when defining procedures. This eliminates the need to put the proc commands inside a namespace block. However, you still need to use namespace eval to create the namespace before you can create procedures inside it. Example 14-2 repeats the random number generator using qualified names. random::init does not need a variable command because it uses a qualified name for seed: Example 14-2 Random number generator using qualified names.
namespace eval random { # Create a variable inside the namespace variable seed [clock seconds] } # Create procedures inside the namespace proc random::init { seed } { set ::random::seed $seed } proc random::random {} { variable seed set seed [expr ($seed*9301 + 49297) % 233280] return [expr $seed/double(233280)] } proc random::range { range } { expr int([random]*$range) }
Top
Table of Contents
Nested Namespaces
Namespaces can be nested inside other namespaces. Example 14-3 shows three namespaces that have their own specific variable x. The fully qualified names for these variables are ::foo::x, ::bar::x, and ::bar::foo::x. Example 14-3 Nested namespaces. namespace eval foo { variable x 1 ;# ::foo::x } namespace eval bar { variable x 2 ;# ::bar::x namespace foo { variable x 3 ;# ::bar::foo::x } puts $foo::x ;# prints 3 } puts $foo::x ;# prints 1 Partially qualified names can refer to two different objects.
In Example 14-3 the partially qualified name foo::x can reference one of two variables depending on the current namespace. From the global scope the name foo::x refers to the namespace variable x inside ::foo. From the ::bar namespace, foo::x refers to the variable x inside ::bar::foo. If you want to unambiguously name a variable in the current namespace, you have two choices. The simplest is to bring the variable into scope with the variable command:
variable x set x something If you need to give out the name of the variable, then you have two choices. The most general solution is to use the namespace current command to create a fully qualified name: trace variable [namespace current]::x r \ [namespace current]::traceproc However, it is simpler to just explicitly write out the namespace as in: trace variable ::myname::x r ::myname::traceproc The drawback of this approach is that it litters your code with references to ::myname::, which might be subject to change during program development.
Top
Table of Contents
When you create the pkgIndex.tcl package index file with pkg_mkIndex, which is described Chapter 12, you should be aware that only exported names appear in the index. Because of this, I often resort to exporting everything. I never plan to import the names, but I do rely on automatic code loading based on the index files. This exports everything: namespace export * The namespace import command makes commands in another namespace visible in the current namespace. An import can cause conflicts with commands in the current namespace. The namespace
command raises an error if there is a conflict. You can override this with the -force option. The general form of the command is:
import
namespace import ?-force? namespace::pat ?namespace::pat?... The pat is a string match type pattern that is matched against exported commands defined in namespace. You cannot use patterns to match namespace. The namespace can be a fully or partially qualified name of a namespace. If you are lazy, you can import all procedures from a namespace: namespace import random::* The drawback of this approach is that random exports an init procedure, which might conflict with another module you import in the same way. It is safer to import just the procedures you plan on using: namespace import random::random random::range A namespace import takes a snapshot.
If the set of procedures in a namespace changes, or if its export list changes, then this has no effect on any imports that have already occurred from that namespace.
Top
Table of Contents
Commands like after, bind, and button take arguments that are Tcl scripts that are evaluated later. These callback commands execute later in the global scope by default. If you want a callback to be evaluated in a particular namespace, you can construct the callback with namespace code. This command does not execute the callback. Instead, it generates a Tcl command that will execute in the current namespace scope when it is evaluated later. For example, suppose ::current is the current namespace. The namespace code command determines the current scope and adds that to the namespace inscope command it generates: set callback [namespace code {set x 1}] => namespace inscope ::current {set x 1} # sometime later ... eval $callback When you evaluate $callback later, it executes in the ::current namespace because of the namespace inscope command. In particular, if there is a namespace variable ::current::x , then that variable is modified. An alternative to using namespace code is to name the variable with a qualified name: set callback {set ::current::x 1} The drawback of this approach is that it makes it tedious to move the code to a different namespace. If you need substitutions to occur on the command when you define it, use list to construct it. Using list is discussed in more detail on pages 123 and 389. Example 14-4 wraps up the list and the namespace inscope into the code procedure, which is handy because you almost always want to use
list when
constructing callbacks. The uplevel in code ensures that the correct namespace is captured; you can use code anywhere: Example 14-4 The code procedure to wrap callbacks. proc code {args} { set namespace [uplevel {namespace current}] return [list namespace inscope $namespace $args] } namespace eval foo { variable y "y value" x {} set callback [code set x $y] => namespace inscope ::foo {set x {y value}} } The example defines a callback that will set ::foo::x to y value. If you want to set x to the value that y has at the time of the callback, then you do not want to do any substitutions. In that case, the original namespace code is what you want: set callback [namespace code {set x $y}] => namespace inscope ::foo {set x $y} If the callback has additional arguments added by the caller, namespace inscope correctly adds them. For example, the scrollbar protocol described on page 431 adds parameters to the callback that controls a scrollbar.
Top
Table of Contents
Introspection
The info commands operation returns all the commands that are currently visible. It is described in more detail on page 179. You can limit the information returned with a string match pattern. You can also include a namespace specifier in the pattern to see what is visible in a namespace. Remember that global commands and imported commands are visible, so info commands returns more than just what is defined by the namespace. Example 14-5 uses namespace origin, which returns the original name of imported commands, to sort out the commands that are really defined in a namespace: Example 14-5 Listing commands defined by a namespace. proc Namespace_List {{namespace {}}} { if {[string length $namespace] == 0} { # Determine the namespace of our caller set namespace [uplevel {namespace current}] } set result {} foreach cmd [info commands ${namespace}::*] { if {[namespace origin $cmd] == $cmd} { lappend result $cmd } } return [lsort $result] }
Top
Table of Contents
Returns the current namespace. Returns names of nested namespaces. name defaults to current namespace. pat is a string match pattern that limits what is returned. Generates a namespace inscope command that will eval script in the current namespace. Deletes the variables and commands from the specified namespaces. Concatenates args, if present, onto cmd and evaluates it in name namespace. Adds patterns to the export list for current namespace. Returns export list if no patterns. Undoes the import of names matching patterns. Adds the names matching the patterns to the current namespace. Appends args, if present, onto cmd as list elements and evaluates it in name namespace. Returns the original name of cmd. Returns the parent namespace of name, or of the current namespace.
namespace delete name ? name? ... namespace eval name cmd ? args? ... namespace export ?clear? ?pat? ?pat? ... namespace forget pat ? pat? ... namespace import ?force? pat ?pat? ... namespace inscope name cmd ?args? ... namespace origin cmd namespace parent ?name?
namespace qualifiers name namespace which ?flag? name namespace tail name
Returns the part of name up to the last :: in it. Returns the fully qualified version of name. The flag is one of command , -variable, or -namespace. Returns the last component of name.
Top
Table of Contents
Top
Table of Contents
System
The Tcl namespace facility does not provide classes and inheritance. It just provides new scopes and a way to hide procedures and variables inside a scope. There are Tcl C APIs that support hooks in variable name and command lookup for object systems so that they can implement classes and inheritance. By exploiting these interfaces, various object systems can be added to Tcl as shared libraries. The Tcl namespace facility was proposed by Michael McLennan based on his experiences with [incr Tcl], which is the most widely used object-oriented extension for Tcl. [incr Tcl] provides classes, inheritance, and protected variables and commands. If you are familiar with C++, [incr Tcl] should feel similar. A complete treatment of [incr Tcl] is not made in this book. Tcl/Tk Tools (Mark Harrison, O'Reilly & Associates, Inc., 1997) is an excellent source of information. You can find a version of [incr Tcl] on the CD-ROM. The [incr Tcl] home page is: http://www.tcltk.com/itcl/
Top
Table of Contents
Notes
The final section of this chapter touches on a variety of features of the namespace facility.
pattern. Packages like [incr Tcl] exploit this hook to implement more elaborate schemes. The auto_import hook was first introduced in Tcl 8.0.3.
Naming Quirks
When you name a namespace, you are allowed to have extra colons at the end. You can also have two or more colons as the separator between namespace name components. These rules make it easier to assemble names by adding to the value returned from namespace current. These all name the same namespace: ::foo::bar ::foo::bar:: ::foo:::::::bar The name of the global namespace can be either :: or the empty string. This follows from the treatment of :: in namespace names. When you name a variable or command, a trailing :: is significant. In the following command a variable inside the ::foo::bar namespace is modified. The variable has an empty string for its name! set ::foo::bar:: 3 namespace eval ::foo::bar { set {} } => 3 If you want to embed a reference to a variable just before two colons, use a backslash to turn off the variable name parsing before the colons: set x xval set y $x\::foo => xval::foo
Miscellaneous
You can remove names you have imported:
namespace forget random::init You can rename imported procedures to modify their names: rename range Range You can even move a procedure into another namespace with rename: rename random::init myspace::init
Top
Table of Contents
Top
Table of Contents
The "cp" is short for "code page," the term that Windows uses to refer to different encodings. On my Unix system, the system encoding is iso8859-1. Do not change the system encoding.
You could also change the system encoding with: encoding system encoding But this is not a good idea. It immediately changes how Tcl passes strings to your operating system, and it is likely to leave Tcl in an unusable state. Tcl automatically determines the system encoding for you. Don't bother trying to set it yourself. The encoding names command lists all the encodings that Tcl knows about. The encodings are kept in files stored in the encoding directory under the Tcl script library. They are loaded automatically the first time you use an encoding. lsort [encoding names] => ascii big5 cp1250 cp1251 cp1252 cp1253 cp1254 cp1255 cp1256 cp1257 cp1258 cp437 cp737 cp775 cp850 cp852 cp855 cp857 cp860 cp861 cp862 cp863 cp864 cp865 cp866 cp869 cp874 cp932 cp936 cp949 cp950 dingbats euc-cn euc-jp euc-kr gb12345 gb1988 gb2312 identity iso2022 iso2022-jp iso2022-kr iso8859-1 iso8859-2 iso8859-3 iso8859-4 iso8859-5 iso8859-6 iso8859-7 iso8859-8 iso8859-9 jis0201 jis0208 jis0212 ksc5601 macCentEuro macCroatian macCyrillic macDingbats macGreek macIceland macJapan macRoman macRomania macThai macTurkish macUkraine shiftjis symbol unicode utf-8 The encoding names reflect their origin. The "cp" refers to the "code pages" that Windows uses to manage encodings. The "mac" encodings come from the Macintosh. The "iso," "euc," "gb," and "jis" encodings come from various standards bodies.
set in [open README.russian] fconfigure $in -encoding iso8859-7 Example 15-1 shows a simple utility I use in exmh,[*] a MIME-aware mail reader. MIME has its own convention for specifying the character set encoding of a mail message that differs slightly from Tcl's naming convention. The procedure launders the name and then sets the encoding. Exmh was already aware of MIME character sets, so it could choose fonts for message display. Adding this procedure and adding two calls to it was all I had to do adapt exmh to Unicode.
[*] The
exmh home page is http://www.beedub.com/exmh/. It is a wonderful tool that helps me manage tons of e-mail. It is written in Tcl/Tk, of course, and relies on the MH mail system, which limits it to UNIX.
Example 15-1 MIME character sets.and file encodings. proc Mime_SetEncoding {file charset} { regsub -all {(iso|jis|us)-} $charset {\1}charset set charset [string tolower charset] regsub usascii $charset ascii charset fconfigure $file -encoding $charset }
} }
Converts binary data from the specified encoding, which defaults to the system encoding, into Unicode. Converts string from Unicode into data in the encoding format, which defaults to the system encoding. Returns the names of known encodings. Queries or change the system encoding.
Top
Table of Contents
Message Catalogs
A message catalog is a list of messages that your application will display. The main idea is that you can maintain several catalogs, one for each language you support. Unfortunately, you have to be explicit about using message catalogs. Everywhere you generate output or display strings in Tk widgets, you need to change your code to go through a message catalog. Fortunately, Tcl uses a nice trick to make this fairly easy and to keep your code readable. Instead of using keys like "message42" to get messages out of the catalog, Tcl just uses the strings you would use by default. For example, instead of this code: puts "Hello, World!" A version that uses message catalogs looks like this: puts [msgcat::mc "Hello, World!"] If you have not already loaded your message catalog, or if your catalog doesn't contain a mapping for "Hello, World!", then msgcat::mc just returns its argument. Actually, you can define just what happens in the case of unknown inputs by defining your own msgcat::mcunknown procedure, but the default behavior is quite good. The message catalog is implemented in Tcl in the msgcat package. You need to use package require to make it available to your scripts: package require msgcat In addition, all the procedures in the package begin with "mc," so you can use namespace import to shorten their names further. I am not a big fan of namespace import, but if you use message catalogs, you will be calling the msgcat::mc function a lot, so it may be worthwhile to import it:
Specifying a Locale
A locale identifies a language or language dialect to use in your output. A three-level scheme is used in the locale identifier: language_country_dialect The language codes are defined by the ISO-3166 standard. For example, "en" is English and "es" is Spanish. The country codes are defined by the ISO-639 standard. For example, US is for the United States and UK is for the United Kingdom. The dialect is up to you. The country and dialect parts are optional. Finally, the locale specifier is case insensitive. The following examples are all valid locale specifiers: es en en_US en_us en_UK en_UK_Scottish en_uk_scottish Users can set their initial locale with the LANG and LOCALE environment variables. If there is no locale information in the environment, then the "c" locale is used (i.e., the C programming language.) You can also set and query the locale with the msgcat::mclocale procedure: msgcat::mclocale => c msgcat::mclocale en_US The msgcat::mcpreferences procedure returns a list of the user's locale preferences from most specific (i.e., including the dialect) to most general (i.e., only the language). For example: msgcat::mclocale en_UK_Scottish msgcat::mcpreferences => en_UK_Scottish en_UK en
define entries in the catalog. The syntax of the msgcat::mcset procedure is: msgcat::mcset locale src-string ?dest-string? The locale is a locale description like es or en_US_Scottish. The src-string is the string used as the key when calling msgcat::mc. The dest-string is the result of msgcat::mc when the locale is in force. The msgcat::mcload procedure should be used to load your message catalog files. It expects the files to be named according to their locale (e.g., en_US_Scottish.msg), and it binds the message catalog to the current namespace. The msgcat::mcload procedure loads files that match the msgcat::mcpreferences and have the .msg suffix. For example, with a locale of en_UK_Scottish, msgcat::mcload would look for these files: en_UK_Scottish.msg en_UK.msg en.msg The standard place for message catalog files is in the msgs directory below the directory containing a package. With this arrangement you can call msgcat::mcload as shown below. The use of info script to find related files is explained on page 181. msgcat::mcload [file join [file dirname [info script]] msgs] The message catalog file is sourced, so it can contain any Tcl commands. You might find it convenient to import the msgcat::mcset procedure. Be sure to use -force with namespace import because that command might already have been imported as a result of loading other message catalog files. Example 15-3 shows three trivial message catalog files: Example 15-3 Three sample message catalog files. ## en.msg namespace import -force msgcat::mcset mcset mcset mcset # end en en en of Hello Hello_en Goodbye Goodbye_en String String_en en.msg
## en_US.msg namespace import -force msgcat::mcset mcset en_US Hello Hello_en_US mcset en_US Goodbye Goodbye_en_US
# end of en_US.msg ## en_US_Texan.msg namespace import -force msgcat::mcset mcset en_US_Texan Hello Howdy! # end of en_US_Texan.msg Assuming the files from Example 15-3 are all in the msgs directory below your script, you can load all these files with these commands: msgcat::mclocale en_US_Texan msgcat::mcload [file join [file dirname [info script]] msgs] The dialect has the highest priority: msgcat::mc Hello => Howdy! If the dialect does not specify a mapping, then the country mapping is checked: msgcat::mc Goodbye => Goodbye_en_US Finally, the lowest priority is the language mapping: msgcat::mc String => String_en
What happens is that msgcat::mcset and msgcat::mc are sensitive to the current Tcl namespace. Namespaces are described in detail in Chapter 14. If the foo package loads its message catalog while inside the foo namespace, then any calls to msgcat::mc from inside the foo namespace will see those definitions. In fact, if you call msgcat::mc from inside any namespace, it will find only message catalog definitions defined from within that namespace. If you want to share message catalogs between namespaces, you will need to implement your own version of msgcat::mcunknown that looks in the shared location. Example 15-4 shows a version that looks in the global namespace before returning the default string. Example 15-4 Using msgcat::mcunknown to share message catalogs. proc msgcat::mcunknown {local src} { variable insideUnknown if {![info exist insideUnknown]} { # Try the global namespace, being careful to note # that we are already inside this procedure. set insideUnknown true set result [namespace eval :: [list \ msgcat::mc $src \ ]] unset insideUnknown return $result } else { # Being called because the message isn't found # in the global namespace return $src } }
Returns the translation of src according to the current locale and namespace. Queries or set the current locale. Returns a list of locale preferences ordered from the most specific to the most general. Loads message files for the current locale from directory. Defines a mapping for the src string in locale to the translation string. This procedure is called to resolve unknown translations. Applications can provide their own implementations.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
after milliseconds after ms arg ? arg...? after cancel id after cancel command after idle command after info ?id?
Pauses for milliseconds. Concatenates the args into a command and executes it after ms milliseconds. Immediately returns an ID. Cancels the command registered under id. Cancels the registered command. Runs command at the next idle moment. Returns a list of IDs for outstanding after events, or the command associated with id.
Top
Table of Contents
You should check for end of file in your read handler because it will be called when end of file occurs. It is important to close the channel inside the handler because closing the channel automatically unregisters the handler. If you forget to close the channel, your read event handler will be called repeatedly. Example 16-1 shows a read event handler. A pipeline is opened for reading and its command executes in the background. The Reader command is invoked when data is available on the pipe. When end of file is detected a variable is set, which signals the application waiting with vwait. Otherwise, a single
line of input is read and processed. The vwait command is described on the next page. Example 22-1 on page 318 also uses fileevent to read from a pipeline. Example 16-1 A read event file handler. proc Reader { pipe } { global done if {[eof $pipe]} { catch {close $pipe} set done 1 return } gets $pipe line # Process the line here... } set pipe [open "|some command"] fileevent $pipe readable [list Reader $pipe] vwait done There can be at most one read handler and one write handler for an I/O channel. If you register a handler and one is already registered, then the old registration is removed. If you call fileevent without a command argument, it returns the currently registered command, or it returns the empty string if there is none. If you register the empty string, it deletes the current file handler. Table 16-2 summarizes the fileevent command.
Queries or registers command to be called when fileId is readable. Queries or registers command to be called when fileId is writable.
Top
Table of Contents
global Stdin if {[eof stdin]} { exit } append Stdin(line) [gets stdin] if {[info complete $Stdin(line)]} { catch {uplevel #0 $Stdin(line)}result puts $result puts -nonewline $prompt flush stdout set Stdin(line) {} } else { append Stdin(line) \n } }
Top
Table of Contents
Blocks until I/O channel is ready: 0 or 1. Buffer mode: none, line, or full. Number of characters in the buffer. Special end of file character. Control-z (\x1a) for DOS. Null otherwise. The character set encoding. Returns the last POSIX error message associated with a channel. End of line translation: auto, lf, cr, crlf, binary. Serial devices only. Format: baud,parity,data,stop Sockets only. IP address of remote host. Sockets only. Port number of remote host.
Nonblocking I/O
By default, I/O channels are blocking. A gets or read will wait until data is available before returning. A puts may also wait if the I/O channel is not ready to accept data. This behavior is all right if you are using disk files, which are essentially always ready. If you use pipelines or network sockets, however, the blocking behavior can hang up your application. The fconfigure command can set a channel into nonblocking mode. A gets or read command may return immediately with no data. This occurs when there is no data available on a socket or pipeline. A puts to a nonblocking channel will accept all the data and buffer it internally. When the underlying device (i.e., a pipeline or socket) is ready, then Tcl automatically writes out the buffered data. Nonblocking channels are useful because your application can do something else while waiting for the I/O channel. You can also manage several nonblocking I/O channels at once. Nonblocking channels should be used with the fileevent command described earlier. The following command puts a channel into nonblocking mode: fconfigure fileID -blocking 0 It is not strictly necessary to put a channel into nonblocking mode if you use fileevent. However, if the channel is in blocking mode, then it is still possible for the gets or read done by your fileevent procedure to block. For example, an I/O channel might have some data ready, but not a complete line. In this case, a gets would block, unless the channel is nonblocking. Perhaps the best motivation for a nonblocking channel is the buffering behavior of a nonblocking puts. You can even close a channel that has buffered data, and Tcl will automatically write out the buffers as the channel becomes ready. For these reasons, it is common to use a nonblocking channel with fileevent. Example 16-3 shows a fileevent handler for a nonblocking channel. As described above, the gets may not find a complete line, in which case it doesn't read anything and returns -1. Example 16-3 A read event file handler for a nonblocking channel. set pipe [open "|some command"] fileevent $pipe readable [list Reader $pipe] fconfigure $pipe -blocking 0 proc Reader { pipe } { global done if {[eof $pipe]} { catch {close $pipe} set done 1 return } if {[gets $pipe line] < 0} { # We blocked anyway because only part of a line # was available for input } else { # Process one line } } vwait done
Buffering
By default, Tcl buffers data, so I/O is more efficient. The underlying device is accessed less frequently, so there is less overhead. In some cases you may want data to be visible immediately and buffering gets in the way. The following turns off all buffering: fconfigure fileID -buffering none Full buffering means that output data is accumulated until a buffer fills; then a write is performed. For reading, Tcl attempts to read a whole buffer each time more data is needed. The read-ahead for buffering will not block. The -buffersize parameter controls the buffer size: fconfigure fileID -buffering full -buffersize 8192 Line buffering is used by default on stdin and stdout. Each newline in an output channel causes a write operation. Read buffering is the same as full buffering. The following command turns on line buffering: fconfigure fileID -buffering line
No translation at all. UNIX-style, which also means no translations. Macintosh style. On input, carriage returns are converted to newlines. On output, newlines are converted to carriage returns. Windows and Network style. On input, carriage return, newline is converted to a newline. On output, a newline is converted to a carriage return, newline. The default behavior. On input, all end of line conventions are converted to a newline. Output is in native format.
Serial Devices
The -mode attribute specifies the baud rate, parity mode, the number of data bits, and the number of stop bits: set tty [open /dev/ttya] fconfigure $tty -mode => 9600,0,8,2 If you need to set additional attributes of the serial channel, you will have to write a command in C that makes the system calls you need. If you are familiar with serial devices, you know there are quite a few possibilities! Windows has some special device names that always connect you to the serial line devices when you use open. They are com1 through com8. The system console is named con. The null device is nul. UNIX has names for serial devices in /dev. The serial devices are /dev/ttya, /dev/ttyb, and so on. The system console is /dev/console. The current terminal is /dev/tty. The null device is /dev/null. Macintosh needs a special command to open serial devices. This is provided by a third-party extension that you can find at the Tcl Resource Center under: http://www.scriptics.com/resource/software/extensions/macintosh
Top
Table of Contents
The Scotty Tcl extension provides access to other network protocols like UDP, DNS, and RPC. It also supports the SNMP network management protocol and the MIB database associated with SNMP. Scotty is a great extension package that is widely used for network management applications. Its home page is: http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/
Top
Table of Contents
Client Sockets
A client opens a socket by specifying the host address and port number for the server of the socket. The host address gives the network location (i.e., which computer), and the port selects a particular server from all the possible servers that may be running on that host. For example, HTTP servers typically use port 80, while FTP servers use port 20. The following example shows how to open a client socket to a Web server: set s [socket www.scriptics.com 80] There are two forms for host names. The previous example uses a domain name: www.scriptics.com. You can also specify raw IP addresses, which are specified with four dot-separated integers (e.g., 128.15.115.32). A domain name is mapped into a raw IP address by the system software, and it is almost always a better idea to use a domain name in case the IP address assignment for the host changes. This can happen when hosts are upgraded or they move to a different part of the network. As of Tcl 8.2, there is no direct access from Tcl to the DNS service that maps host names to IP addresses. You'll need to use Scotty to get DNS access. Some systems also provide symbolic names for well-known port numbers. For example, instead of using 20 for the FTP service, you can use ftp. On UNIX systems, the well-known port numbers are listed in the file named /etc/services.
domain name or an IP address. If your application needs a specific client port, it can choose one with the -myport option. If the port is in use, the socket command will raise an error. The -async option causes connection to happen in the background, and the socket command returns immediately. The socket becomes writable when the connection completes, or fails. You can use fileevent to get a callback when this occurs. This is shown in Example 17-1. If you use the socket before the connection completes, and the socket is in blocking mode, then Tcl automatically blocks and waits for the connection to complete. If the socket is in nonblocking mode, attempts to use the socket return immediately. The gets and read commands would return -1, and fblocked would return 1 in this situation. In some cases, it can take a long time to open the connection to the server. Usually this occurs when the server host is down, and it may take longer than you want for the connection to time out. The following example sets up a timer with after so that you can choose your own timeout limit on the connection: Example 17-1 Opening a client socket with a timeout. proc Socket_Client {host port timeout} { global connected after $timeout {set connected timeout} set sock [socket -async $host $port] fileevent $sock w {set connected ok} vwait connected if {$connected == "timeout"} { return -code error timeout } else { return $sock } }
Top
Table of Contents
Server Sockets
A TCP server socket allows multiple clients. The way this works is that the socket command creates a listening socket, and then new sockets are created when clients make connections to the server. Tcl takes care of all the details and makes this easy to use. You simply specify a port number and give the socket command a callback to execute when a client connects to your server socket. The callback is just a Tcl command. A simple example is shown below: Example 17-2 Opening a server socket. set listenSocket [socket -server Accept 2540] proc Accept {newSock addr port} { puts "Accepted $newSock from $addr port $port" } vwait forever The Accept command is the callback. With server sockets, Tcl adds additional arguments to the callback before it calls it. The arguments are the new socket connection, and the host and port number of the remote client. In this simple example, Accept just prints out its arguments. The vwait command puts Tcl into its event loop so that it can do the background processing necessary to accept connections. The vwait command will wait until the forever variable is modified, which won't happen in this simple example. The key point is that Tcl processes other events (e.g., network connections and other file I/O) while it waits. If you have a Tk application (e.g., wish), then it already has an event loop to handle window system events, so you do not need to use vwait. The Tcl event loop is discussed on page 217.
socket -server callback ?-myaddr address? port The last argument to the socket command is the server's port number. For your own unofficial servers, you'll need to pick port numbers higher than 1024 to avoid conflicts with existing services. UNIX systems prevent user programs from opening server sockets with port numbers less than 1024. If you use 0 as the port number, then the operating system will pick the listening port number for you. You must use fconfigure to find out what port you have: fconfigure $sock -sockname => ipaddr hostname port
Top
Table of Contents
the buffering behavior of the network socket. You can use Example 17-3 as a template for more interesting services. The Echo_Server procedure opens the socket and saves the result in echo(main). When this socket is closed later, the server stops accepting new connections but existing connections won't be affected. If you want to experiment with this server, start it and wait for connections like this: Echo_Server 2540 vwait forever The EchoAccept procedure uses the fconfigure command to set up line buffering. This means that each puts by the server results in a network transmission to the client. The importance of this will be described in more detail later. A complete description of the fconfigure command is given in Chapter 16. The EchoAccept procedure uses the fileevent command to register a procedure that handles I/O on the socket. In this example, the Echo procedure will be called whenever the socket is readable. Note that it is not necessary to put the socket into nonblocking mode when using the fileevent callback. The effects of nonblocking mode are discussed on page 221.
EchoAccept saves information about each client in the echo array. This is used only to print out a
message when a client closes its connection. In a more sophisticated server, however, you may need to keep more interesting state about each client. The name of the socket provides a convenient handle on the client. In this case, it is used as part of the array index. The Echo procedure first checks to see whether the socket has been closed by the client or there is an error when reading the socket. The if expression only performs the gets if the eof does not return true: if {[eof $sock] || [catch {gets $sock line}]} { Closing the socket automatically clears the fileevent registration. If you forget to close the socket upon the end of file condition, the Tcl event loop will invoke your callback repeatedly. It is important to close it when you detect end of file. Example 17-4 A client of the echo service. proc Echo_Client {host port} { set s [socket $host $port] fconfigure $s -buffering line return $s } set s [Echo_Client localhost 2540] puts $s "Hello!" gets $s => Hello! In the normal case, the server simply reads a line with gets and then writes it back to the client with
puts. If the line is "quit," then the server closes its main socket. This prevents any more connections
by new clients, but it doesn't affect any clients that are already connected. Example 17-4 shows a sample client of the Echo service. The main point is to ensure that the socket is line buffered so that each puts by the client results in a network transmission. (Or, more precisely, each newline character results in a network transmission.) If you forget to set line buffering with fconfigure, the client's gets command will probably hang because the server will not get any data; it will be stuck in buffers on the client.
Top
Table of Contents
The protocol begins with the client sending a line that identifies the command (GET), the path, and the protocol version. The path is the part of the URL after the server and port specification. The rest of the request is lines in the following format: key: value The Host identifies the server, which supports servers that implement more than one server name. The User-Agent identifies the client program, which is often a browser like Netscape Navigator or Internet Explorer. The key-value lines are terminated with a blank line. This data is flushed out of the Tcl buffering system with the flush command. The server will respond by sending the URL contents back over the socket. This is described shortly, but first we consider proxies.
Proxy Servers
A proxy is used to get through firewalls that many organizations set up to isolate their network from the Internet. The proxy accepts HTTP requests from clients inside the firewall and then forwards the requests outside the firewall. It also relays the server's response back to the client. The protocol is nearly the same when using the proxy. The difference is that the complete URL is passed to the GET command so that the proxy can locate the server. Example 17-6 uses a proxy if one is defined: Example 17-6 Opening a connection to an HTTP server. # Http_Proxy sets or queries the proxy proc Http_Proxy {{new {}}} { global http if ![info exists http(proxy)] { return {} } if {[string length $new] == 0} { return $http(proxy):$http(proxyPort) } else { regexp {^([^:]+):([0-9]+)$}$new x \ http(proxy) http(proxyPort) } } proc Http_Open {url {cmd GET} {query {}}} { global http if {![regexp -nocase {^(http://)?([^:/]+)(:([0-9])+)?(/.*)}\ $url x protocol server y port path]} { error "bogus URL: $url" } if {[string length $port] == 0} { set port 80 } if {[info exists http(proxy)] && [string length $http(proxy)]} {
set sock [socket $http(proxy) $http(proxyPort)] puts $sock "$cmd http://$server:$port$path HTTP/1.0" } else { set sock [socket $server $port] puts $sock "$cmd $path HTTP/1.0" } puts $sock "User-Agent: Tcl/Tk Http_Open" puts $sock "Host: $server" if {[string length $query] > 0} { puts $sock "Content-Length: [string length $query]" puts $sock "" puts $sock $query } puts $sock "" flush $sock fconfigure $sock -blocking 0 return $sock }
if {[catch {gets $state(sock) line}nbytes]} { set state(status) error lappend state(headers) [list error $nbytes] close $state(sock) return } if {$nbytes < 0} { # Read would block return } elseif {$nbytes == 0} { # Header complete set state(status) head } elseif {![info exists state(headers)]} { # Initial status reply from the server set state(headers) [list http $line] } else { # Process key-value pairs regexp {^([^:]+): *(.*)$}$line x key value lappend state(headers) [string tolower $key] $value } } The Http_Head procedure uses Http_Open to contact the server. The HttpHeader procedure is registered as a fileevent handler to read the server's reply. A global array keeps state about each operation. The URL is used in the array name, and upvar is used to create an alias to the name (upvar is described on page 86): upvar #0 $url state You cannot use the upvar alias as the variable specified to vwait. Instead, you must use the actual name. The backslash turns off the array reference in order to pass the name of the array element to vwait, otherwise Tcl tries to reference url as an array: vwait $url\(status) The HttpHeader procedure checks for special cases: end of file, an error on the gets, or a short read on a nonblocking socket. The very first reply line contains a status code from the server that is in a different format than the rest of the header lines: code message The code is a three-digit numeric code. 200 is OK. Codes in the 400's and 500's indicate an error. The codes are explained fully in RFC 1945 that specifies HTTP 1.0. The first line is saved with the key http:
set state(headers) [list http $line] The rest of the header lines are parsed into key-value pairs and appended onto state(headers). This format can be used to initialize an array: array set header $state(headers) When HttpHeader gets an empty line, the header is complete and it sets the state(status) variable, which signals Http_Head. Finally, Http_Head returns the status to its caller. The complete information about the request is still in the global array named by the URL. Example 17-8 illustrates the use of Http_Head: Example 17-8 Using Http_Head. set url http://www.sun.com/ set status [Http_Head $url] => eof upvar #0 $url state array set info $state(headers) parray info info(http) HTTP/1.0 200 OK info(server) Apache/1.1.1 info(last-modified) Nov ... info(content-type) text/html
Chapter 3.
application/postscript application/x-tcl
? a postscript document.
Example 17-9 Http_Get fetches the contents of a URL. proc Http_Get {url {query {}}} { upvar #0 $url state ;# Alias to global array catch {unset state} ;# Aliases still valid. if {[string length $query] > 0} { set state(sock) [Http_Open $url POST $query] } else { set state(sock) [Http_Open $url GET] } set sock $state(sock) fileevent $sock readable [list HttpHeader $url] # Specify the real name, not the upvar alias, to vwait vwait $url\(status) set header(content-type) {} set header(http) "500 unknown error" array set header $state(headers) # Check return status. # 200 is OK, other codes indicate a problem. regsub "HTTP/1.. " $header(http) {}header(http) if {![string match 2* $header(http)]} { catch {close $sock} if {[info exists header(location)] && [string match 3* $header(http)]} { # 3xx is a redirection to another URL set state(link) $header(location) return [Http_Get $header(location) $query] } return -code error $header(http) } # Set up to read the content data switch -glob -- $header(content-type) { text/* { # Read HTML into memory fileevent $sock readable [list HttpGetText $url] } default { # Copy content data to a file fconfigure $sock -translation binary set state(filename) [File_TempName http] if [catch {open $state(filename) w}out] { set state(status) error set state(error) $out close $sock
return $header(content-type) } set state(fd) $out fcopy $sock $out -command [list HttpCopyDone $url] } } vwait $url\(status) return $header(content-type) }
Http_Get uses Http_Open to
initiate the request, and then it looks for errors. It handles redirection errors that occur if a URL has changed. These have error codes that begin with 3. A common case of this error is when a user omits the trailing slash on a URL (e.g., http://www.scriptics.com). Most servers respond with: 302 Document has moved Location: http://www.scriptics.com/ If the content-type is text, then Http_Get sets up a fileevent handler to read this data into memory. The socket is in nonblocking mode, so the read handler can read as much data as possible each time it is called. This is more efficient than using gets to read a line at a time. The text will be stored in the state(body) variable for use by the caller of Http_Get. Example 17-10 shows the HttpGetText fileevent handler: Example 17-10 HttpGetText reads text URLs. proc HttpGetText {url} { upvar #0 $url state if {[eof $state(sock)]} { # Content complete set state(status) done close $state(sock) } elseif {[catch {read $state(sock)}block]} { set state(status) error lappend state(headers) [list error $block] close $state(sock) } else { append state(body) $block } } The content may be in binary format. This poses a problem for Tcl 7.6 and earlier. A null character will terminate the value, so values with embedded nulls cannot be processed safely by Tcl scripts. Tcl 8.0 supports strings and variable values with arbitrary binary data. Example 17-9 uses fcopy to copy data from the socket to a file without storing it in Tcl variables. This command was introduced in Tcl 7.5 as unsupported0, and became fcopy in Tcl 8.0. It takes a callback argument that is invoked when
the copy is complete. The callback gets additional arguments that are the bytes transferred and an optional error string. In this case, these arguments are added to the url argument specified in the fcopy command. Example 17-11 shows the HttpCopyDone callback: Example 17-11 HttpCopyDone is used with fcopy. proc HttpCopyDone {url bytes {error {}}} { upvar #0 $url state if {[string length $error]} { set state(status) error lappend state(headers) [list error $error] } else { set state(status) ok } close $state(sock) close $state(fd) } The user of Http_Get uses the information in the state array to determine the status of the fetch and where to find the content. There are four cases to deal with: There was an error, which is indicated by the state(error) element. There was a redirection, in which case, the new URL is in state(link). The client of Http_Get should change the URL and look at its state instead. You can use upvar to redefine the alias for the state array: upvar #0 $state(link) state There was text content. The content is in state(body). There was another content type that was copied to state(filename).
fcopy $in $out -command [list CopyDone $in $out] proc CopyDone {in out bytes {error {}} { close $in ; close $out } With a background copy, the fcopy command transfers data from input until end of file or size bytes have been transferred. If no -size argument is given, then the copy goes until end of file. It is not safe to do other I/O operations with input or output during a background fcopy. If either input or output gets closed while the copy is in progress, the current copy is stopped. If the input is closed, then all data already queued for output is written out. Without a -command argument, the fcopy command reads as much as possible depending on the blocking mode of input and the optional size parameter. Everything it reads is queued for output before fcopy returns. If output is blocking, then fcopy returns after the data is written out. If input is blocking, then fcopy can block attempting to read size bytes or until end of file.
Top
Table of Contents
The default proxy filter just returns the -proxyhost and -proxyport values if they are set. You can supply a smarter filter that picks a proxy based on the host in the URL. The proxy filter is called with the hostname and should return a list of two elements, the proxy host and port. If no proxy is required, return an empty list. The -timeout value limits the time the transaction can take. Its value is unlimited for no timeout, or a milliseconds value. You can specify 500, for example, to have a half-second timeout. http::geturl The http::geturl procedure does a GET, POST, or HEAD transaction depending on its arguments. By default, http::geturl blocks until the request completes and it returns a token that represents the transaction. As described below, you use the token to get the results of the transaction. If you supply a -command callback option, then http::geturl returns immediately and invokes callback when the transaction completes. The callback is passed the token that represents the transaction. Table 17-1 lists the options to http::geturl:
Block size when copying to a channel. The fileID is an open file or socket. The URL data is copied to this channel instead of saving it in memory. Calls callback when the transaction completes. The token from http::geturl is passed to callback. Called from the event handler to read data from the URL. The list specifies a set of headers that are included in the HTTP request. The list alternates between header keys and values. Calls command after each block is copied to a channel. It gets called with three parameters:
command token totalsize currentsize
Issues a POST request with the codedstring form data. Aborts the request after msec milliseconds have elapsed. If bool is true, a HEAD request is made.
For simple applications you can simply block on the transaction: set token [http::geturl www.beedub.com/index.html] => http::1
The leading http:// in the URL is optional. The return value is a token that is also the name of a global array that contains state about the transaction. Names like http::1 are used instead of using the URL as the array name. You can use upvar to convert the return value from http::geturl to an array variable: upvar #0 $token state By default, the URL data is saved in state(body). The elements of the state array are described in Table 17-2:
The contents of the URL. The current number of bytes transferred. An explanation of why the transaction was aborted. The HTTP reply status. A list of the keys and values in the reply header. The current status: pending, ok, eof, or reset. The expected size of the returned data. The content type of the returned data. The URL of the request.
A handful of access functions are provided so that you can avoid using the state array directly. These are listed in Table 17-3:
Returns state(body). Returns state(status). Returns state(error). Returns state(http). Blocks until the transaction completes. Unsets the state array named by $token.
You can take advantage of the asynchronous interface by specifying a command that is called when the transaction completes. The callback is passed the token returned from http::geturl so that it can
access the transaction state: http::geturl $url -command [list Url_Display $text $url] proc Url_Display {text url token} { upvar #0 $token state # Display the url in text } You can have http::geturl copy the URL to a file or socket with the -channel option. This is useful for downloading large files or images. In this case, you can get a progress callback so that you can provide user feedback during the transaction. Example 17-12 shows a simple downloading script: Example 17-12 Downloading files with http::geturl. #!/usr/local/tclsh8.0 if {$argc < 2} { puts stderr "Usage: $argv0 url file" exit 1 } set url [lindex $argv 0] set file [lindex $argv 1] set out [open $file w] proc progress {token total current} { puts -nonewline "." } http::config -proxyhost webcache.eng -proxyport 8080 set token [http::geturl $url -progress progress \ -headers {Pragma no-cache}-channel $out] close $out # Print out the return header information puts "" upvar #0 $token state puts $state(http) foreach {key value}$state(meta) { puts "$key: $value" } exit 0 http::formatQuery If you specify form data with the -query option, then http::geturl does a POST transaction. You need to encode the form data for safe transmission. The http::formatQuery procedure takes a list of keys and values and encodes them in x-www-url-encoded format. Pass this result as the query data: http::formatQuery name "Brent Welch" title "Tcl Programmer" => name=Brent+Welch&title=Tcl+Programmer
http::reset You can cancel an outstanding transaction with http::reset: http::reset $token This is done automatically when you setup a -timeout with http::config. http::cleanup When you are done with the data returned from http::geturl, use the http::cleanup procedure to unset the state variable used to store the data.
Top
Table of Contents
Basic Authentication
Web pages are often password protected. The most common form of this uses a protocol called Basic Authentication, which is not very strong, but easy to implement. With this scheme, the server responds to an HTTP request with a 401 error status and a Www-Authenticate header, which specifies the authentication protocol the server wants to use. For example, the server response can contain the following information: HTTP/1.0 401 Authorization Required Www-Authenticate: Basic realm="My Pages" The realm is meant to be an authentication domain. In practice, it is used in the string that gets displayed to the user as part of the password prompt. For example, a Web browser will display this prompt: Enter the password for My Pages at www.beedub.com After getting the user name and password from the user, the Web browser tries its HTTP request again. This time it includes an Authorization header that contains the user name and password encoded with base64 encoding. There is no encryption at all ?anyone can decode the string, which is why this is not a strong form of protection. The Tcl Web Server includes a base64.tcl file that has Base64_Encode and Base64_Decode procedures. Example 17-13 illustrates the Basic Authentication protocol. http::geturl takes a -headers option that lets you pass additional headers in the request. Example 17-13 Basic Authentication using http::geturl. proc BasicAuthentication {url promptProc} { set token [http::geturl $url] http::wait $token if {[string match 401* [http::status $token]]} { upvar #0 $token data
# Extract the realm from the Www-Authenticate line array set reply $data(meta) if {[regexp {realm=(.*)}$reply(Www-Authenticate) \ x realm]} { # Call back to prompt for username, password set answer [$promptProc $realm] http::cleanup $token # Encode username:password and pass this in # the Authorization header set auth [Base64_Encode \ [lindex $answer 0]:[lindex $answer 1]] set token [http::geturl $url -headers \ [list Authorization $auth]] http::wait $token } } return $token } Example 17-13 takes a promptProc argument that is the name of a procedure to call to get the username and password. This procedure could display a Tk dialog box, or prompt for user input from the terminal. In practice, you probably already know the username and password. In this case, you can skip the initial challengeresponse steps and simply supply the Authorization header on the first request: http::geturl $url -headers \ [list Authorization \ "Basic [Base64_Encode $username:$password"]]
Top
Table of Contents
Top
Table of Contents
TclHttpd Architecture
Figure 18-1 shows the basic components of the server. At the core is the Httpd module (httpd.tcl), which implements the server side of the HTTP protocol. The "d" in Httpd stands for daemon, which is the name given to system servers on UNIX. This module manages network requests, dispatches them to the Url module, and provides routines used to return the results to requests. Figure 18-1. The dotted box represents one application that embeds TclHttpd. Document templates and Application Direct URLs provide direct connections from an HTTP request to your application.
The Url module (url.tcl) divides the Web site into domains, which are subtrees of the URL hierarchy provided by the server. The idea is that different domains may have completely different implementations. For example, the Document domain (doc.tcl) maps its URLs into files and directories on your hard disk, while the Application-Direct domain (direct.tcl) maps URLs into Tcl procedure calls within your application. The CGI domain (cgi.tcl) maps URLs onto other programs that compute web pages.
Top
Table of Contents
Domain Handlers
You can implement new kinds of domains that provide your own interpretation of a URL. This is the most flexible interface available to extend the web server. You provide a callback that is invoked to handle every request in a domain, or subtree, of the URL hierarchy. The callback interprets the URL, computes the page content, and returns the data using routines from the Httpd module. Example 18-1 defines a simple domain that always returns the same page to every request. The domain is registered with the Url_PrefixInstall command. The arguments to Url_PrefixInstall are the URL prefix and a callback that is called to handle all URLs that match that prefix. In the example, all URLs that have the prefix /simple are dispatched to the SimpleDomain procedure. Example 18-1 A simple URL domain. Url_PrefixInstall /simple [list SimpleDomain /simple] proc SimpleDomain {prefix sock suffix} { upvar #0 Httpd$sock data # Generate page header set html "<title>A simple page</title>\n" append html "<h1>$prefix$suffix</h1>\n" append html "<h1>Date and Time</h1>\n" append html [clock format [clock seconds]] # Display query data if {[info exist data(query)]} { append html "<h1>Query Data</h1>\n" append html "<table>\n" foreach {name value}[Url_DecodeQuery $data(query)] { append html "<tr><td>$name</td>\n" append html "<td>$value</td></tr>\n" } append html "</table>\n"
} Httpd_ReturnData $sock text/html $html } The SimpleDomain handler illustrates several properties of domain handlers. The sock and suffix arguments to SimpleDomain are appended by Url_Dispatch when it invokes the domain handler. The suffix parameter is the part of the URL after the prefix. The prefix is passed in as part of the callback definition so the domain handler can recreate the complete URL. For example, if the server receives a request for the URL /simple/page, then the prefix is /simple, the suffix is /request.
Returning Results
Finally, once the page has been computed, the Httpd_ReturnData procedure is used to return the page to the client. This takes care of the HTTP protocol as well as returning the data. There are three related procedures, Httpd_ReturnFile, Httpd_Error, and Httpd_Redirect. These are summarized in Table 18-1 on page 261.
Top
Table of Contents
foreach {name value}$args { append html "<tr><td>$name</td><td>$value</td></tr>\n" } append html "</tr></table>\n" return $html } Example 18-2 defines /demo as an Application Direct URL domain that is implemented by procedures that begin with Demo. There are just three URLs defined: /demo /demo/time /demo/echo The /demo page displays a hypertext link to the /demo/time page and a simple form that will be handled by the /demo/echo page. This page is static, and so there is just one return command in the procedure body. Each line of the string ends with: \n\ This is just a formatting trick to let me indent each line in the procedure, but not have the line indented in the resulting string. Actually, the \-newline will be replaced by one space, so each line will be indented one space. You can leave those off and the page will display the same in the browser, but when you view the page source you'll see the indenting. Or you could not indent the lines in the string, but then your code looks somewhat odd. The /demo/time procedure just returns the result of clock format. It doesn't even bother adding <html>, <head>, or <body> tags, which you can get away with in today's browsers. A simple result like this is also useful if you are using programs to fetch information via HTTP requests.
the ACTION parameter of the FORM tag. Or you could type the following into your browser to embed the query data right into the URL: /demo/param?a=5&b=7&c=red&d=%7ewelch&e=two+words In this case, when your procedure is called, a is 5, b is 7, c is red, and the args parameter becomes a list of: d ~welch e {two words} The %7e and the + are special codes for nonalphanumeric characters in the query data. Normally, this encoding is taken care of automatically by the Web browser when it gets data from a form and passes it to the Web server. However, if you type query data directly or format URLs with complex query data in them, then you need to think about the encoding. Use the Url_Encode procedure to encode URLs that you put into web pages. If parameters are missing from the query data, they either get the default values from the procedure definition or the empty string. Consider this example: /demo/param?b=5 In this case a is "", b is 5, c is cdef, and args is an empty list.
fconfigure $in -translation binary set X [read $in] close $in return $X }
Top
Table of Contents
Document Types
The Document domain (doc.tcl) maps URLs onto files and directories. It provides more ways to extend the server by registering different document type handlers. This occurs in a two-step process. First, the type of a file is determined by its suffix. The mime.types file contains a map from suffixes to MIME types such as text/html or image/gif. This map is controlled by the Mtype module in mtype.tcl. Second, the server checks for a Tcl procedure with the appropriate name: Doc_mimetype The matching procedure, if any, is called to handle the URL request. The procedure should use routines in the Httpd module to return data for the request. If there is no matching Doc_mimetype procedure, then the default document handler uses Httpd_ReturnFile and specifies the Content Type based on the file extension: Httpd_ReturnFile $sock [Mtype $path] $path You can make up new types to support your application. Example 18-4 shows the pieces needed to create a handler for a fictitious document type application/myjunk that is invoked to handle files with the .junk suffix. You need to edit the mime.types file and add a document handler procedure to the server: Example 18-4 A sample document type handler. # Add this line to mime.types application/myjunk .junk # Define the document handler procedure # path is the name of the file on disk # suffix is part of the URL after the domain prefix # sock is the handle on the client connection
proc Doc_application/myjunk {path suffix sock} { upvar #0 Httpd$sock data # data(url) is more useful than the suffix parameter. # Use the contents of file $path to compute a page set contents [somefunc $path] # Determine your content type set type text/html # Return the page Httpd_ReturnData $sock $type $data } As another example, the HTML+Tcl templates use the .tml suffix that is mapped to the application/x-tcl-template type. The TclHttpd distribution also includes support for files with a .snmp extension that implements a template-based web interface to the Scotty SNMP Tcl extension.
Top
Table of Contents
This page describes the steps to take when setting up a new computer in our environment. See <a href=/ordering.html>Ordering Computers</a> for instructions on ordering machines. <ol> <li>Unpack and setup the machine. <li>Use the Network control panel to set the IP address and hostname. <!-- Several steps omitted --> <li>Reboot for the last time. </ol> [SiteFooter] The SitePage procedure takes the page title as an argument. It generates HTML to implement a standard navigational structure. Example 18-7 has a simple implementation of SitePage: Example 18-7 SitePage template procedure. proc SitePage {title} { global site set html "<html><head><title>$title</title></head>\n" append html "<body bgcolor=white text=black>\n" append html "<h1>$title</h1>\n" set sep "" foreach {label url} $site(pages) { append html $sep if {[string compare $label $title] == 0} { append html "$label" } else { append html "<a href='$url'>$label</a>" } set sep " | " } return $html } The foreach loop that computes the simple menu of links turns out to be useful in many places. Example 18-8 splits out the loop and uses it in the Site-Page and SiteFooter procedures. This version of the templates creates a left column for the navigation and a right column for the page content: Example 18-8 SiteMenu and SiteFooter template procedures. proc SitePage {title} { global site set html "<html><head><title>$title</title></head>\n\ <body bgcolor=$site(bg) text=$site(fg)>\n\
<!-- Two Column Layout -->\n\ <table cellpadding=0>\n\ <tr><td>\n\ <!-- Left Column -->\n\ <img src='$site(mainlogo)'>\n\ <font size=+1>\n\ [SiteMenu <br> $site(pages)]\n\ </font>\n\ </td><td>\n\ <!-- Right Column -->\n\ <h1>$title</h1>\n\ <p>\n" return $html } proc SiteFooter {} { global site set html "<p><hr>\n\ <font size=-1>[SiteMenu | $site(pages)]</font>\n\ </td></tr></table>\n" return $html } proc SiteMenu {sep list} { global page set s "" set html "" foreach {label url} $list { if {[string compare $page(url) $url] == 0} { append html $s$label } else { append html "$s<a href='$url'>$label</a>" } set s $sep } return $html } Of course, a real site will have more elaborate graphics and probably a two-level, three-level, or more complex tree structure that describes its structure.You can also define a family of templates so that each page doesn't have to fit the same mold. Once you start using templates, it is fairly easy to change both the template implementation and to move pages around among different sections of your Web site. There are many other applications for "macros" that make repetitive HTML coding chores easy. Take, for example, the link to /ordering.html in Example 18-6. The proper label for this is already defined in $site(pages), so we could introduce a SiteLink procedure that uses this: Example 18-9 The SiteLink procedure. proc SiteLink {label} {
global site array set map $site(pages) if {[info exist map($label)]} { return "<a href='$map($label)'>$label</a>" } else { return $label } } If your pages embed calls to SiteLink, then you can change the URL associated with the page name by changing the value of site(pages). If this is stored in the top-level ".tml" file, the templates will automatically track the changes.
Top
Table of Contents
Form Handlers
HTML forms and form-handling programs go together. The form is presented to the user on the client machine. The form handler runs on the server after the user fills out the form and presses the submit button. The form presents input widgets like radiobuttons, checkbuttons, selection lists, and text entry fields. Each of these widgets is assigned a name, and each widget gets a value based on the user's input. The form handler is a program that looks at the names and values from the form and computes the next page for the user to read. CGI is a standard way to hook external programs to web servers for the purpose of processing form data. CGI has a special encoding for values so that they can be transported safely. The encoded data is either read from standard input or taken from the command line. The CGI program decodes the data, processes it, and writes a new HTML page on its standard output. Chapter 3 describes writing CGI scripts in Tcl. TclHttpd provides alternatives to CGI that are more efficient because they are built right into the server. This eliminates the overhead that comes from running an external program to compute the page. Another advantage is that the Web server can maintain state between client requests in Tcl variables. If you use CGI, you must use some sort of database or file storage to maintain information between requests.
<table> <tr><td>Name</td><td><input name=name></td></tr> <tr><td>Address</td><td><input name=addr1></td></tr> <tr><td> </td><td><input name=addr2></td></tr> <tr><td>City</td><td><input name=city></td></tr> <tr><td>State</td><td><input name=state></td></tr> <tr><td>Zip/Postal</td><td><input name=zip></td></tr> <tr><td>Country</td><td><input name=country></td></tr> </table> </form> The mail message sent by /mail/forminfo is shown in Example 18-11. Example 18-11 Mail message sent by /mail/forminfo. To: mailreader@my.com Subject: Name and Address data { name {Joe Visitor} addr1 {Acme Company} addr2 {100 Main Street} city {Mountain View} state California zip 12345 country USA } It is easy to write a script that strips the headers, defines a data procedure, and uses eval to process the message body. Whenever you send data via e-mail, if you format it with Tcl list structure, you can process it quite easily. The basic structure of such a mail reader procedure is shown in Example 18-12: Example 18-12 Processing mail sent by /mail/forminfo. # Assume the mail message is on standard input set X [read stdin] # Strip off the mail headers, when end with a blank line if {[regsub {.*?\n\ndata} $X {data} X] != 1} { error "Malformed mail message" } proc data {fields} { foreach {name value} $fields { # Do something }
} # Process the message. For added security, you may want # do this part in a safe interpreter. eval $X
} set html "<!-- Self-posting. Next page is $nextpage -->\n" append html "<form action=\"$page(url)\" method=post>\n" append html "<input type=hidden name=formid value=$id>\n" append html "<table border=1>\n" foreach {required key label} $fields { append html "<tr><td>" if {$check && $required && [form::empty $key]} { lappend missing $label append html "<font color=red>*</font>" } append html "</td><td>$label</td>\n" append html "<td><input [form::value $key]></td>\n" append html "</tr>\n" } append html "</table>\n" if {$check} { if {![info exist missing]} { # No missing fields, so advance to the next page. # In practice, you must save the existing fields # at this point before redirecting to the next page. Doc_Redirect $nextpage } else { set msg "<font color=red>Please fill in " append msg [join $missing ", "] append msg "</font>" set html <p>$msg\n$html } } append html "<input type=submit>\n</form>\n" return $html } Example 18-14 shows a page template that calls Form_Simple with the required field description. Example 18-14 A page with a self-checking form. <html><head> <title>Name and Address Form</title> </head> <body bgcolor=white text=black> <h1>Name and Address</h1> Please enter your name and address. [myform::simple nameaddr { 1 name "Name" 1 addr1 "Address" 0 addr2" "Address" 1 city "City"
Top
Table of Contents
Programming Reference
This section summarizes many of the more useful functions defined by the server. These tables are not complete, however. You are encouraged to read through the code to learn more about the features offered by the server. Table 18-1 summarizes the Httpd functions used when returning pages to the client.
Returns a simple error page to the client. The code is a numeric error code like 404 or 500. Returns a page with Content-Type type and content data. Returns a file with Content-Type type. Generates a 302 error return with a Location of newurl. Expands url to include the proper http://server:port prefix to reference the current server.
Table 18-2 summarizes a few useful procedures provided by the Url module (url.tcl). The Url_DecodeQuery is used to decode query data into a Tcl-friendly list. The Url_Encode procedure is useful when encoding values directly into URLs. URL encoding is discussed in more detail on page 249
Decodes a www-url-encoded query string and return a name, value list. Returns value encoded according to the www-url-encoded standard. Registers callback as the handler for all URLs that begin with prefix. The callback is invoked with two additional arguments: sock, the handle to the client, and suffix, the part of the URL after prefix.
The Doc module provides procedures for configuration and generating responses, which are summarized in Tables 18-3 and 18-4 respectively
Sets or queries the directory that corresponds to the root of the URL hierarchy. Maps the file system directory into the URL subtree starting at virtual . Specifies a file relative to the document root used as a simple template for error messages. This is processed by DocSubstSystem file in doc.tcl. If how is 1, then .html files are compared against corresponding .tml files and regenerated if necessary. Registers a file name pattern that will be searched for the default index file in directories. Specifies a file relative to the document root used as a simple template for page not found messages. This is processed by DocSubstSystem file in doc.tcl . Defines the directory used for each users home directory. When a URL like ~user is specified, the dirname under their home directory is accessed. Adds directory to the auto_path so the source files in it are available to the server. Specifies an alternate interpreter in which to process document templates (i.e., .tml files.) Sets or queries the email for the Webmaster.
Doc_PublicHtml dirname
Generates a 500 response on sock based on the template registered with Doc_ErrorPage. errorInfo is a copy of the Tcl error trace after the error. Generates a 404 response on sock by using the template registered with Doc_NotFoundPage. Performs a subst on the file and return the resulting page on sock. interp specifies an alternate Tcl interpreter.
The Doc module also provides procedures for cookies and redirects that are useful in document templates. These are described in Table 18-5.
Returns the cookie name passed to the server for this request, or the empty string if it is not present. Turns off caching of the HTML result. Meant to be called from inside a page template. Returns 1 if the url is a link to the current page. Raises a special error that aborts template processing and triggers a page redirect to newurl. Sets cookie name with the given value that will be returned to the client as part of the response. The path and domain restrict the scope of the cooke. The date sets an expiration date.
Doc_SetCookie -name name -value value -path path -domain domain expires date
Table 18-6 describes the form module that is useful for self-posting forms, which are discussed on page 259
form::data name form::empty name form::value name form::checkvalue name value form::radiovalue name value form::select name valuelist args
Returns the value of the form value name, or the empty string. Returns 1 if the form value name is missing or zero length. Returns name="name" value="value", where value comes from the query data, if any. Returns name="name" value="value" CHECKED, if value is present in the query data for name. Otherwise, it just returns name="name" value="value". Returns name="name" value="value" CHECKED, if the query data for name is equal to value. Otherwise, it just returns name="name" value="value". Generates a select form element with name name. The valuelist determines the option tags and values, and args are optional parameters to the main select tag.
Table 18-7 shows the initial elements of the page array that is defined during the processing of a template.
The decoded query data in a name, value list. If 1, the results of processing the template are not cached in the corresponding .html file. The file system pathname of the requested file (e.g., /usr/local/htdocs/tclhttpd/index.html ). The file system pathname of the template file (e.g., /usr/local/htdocs/tclhttpd/index.tml). The part of the url after the server name (e.g., /tclhttpd/index.html). A relative path from the template file back to the root of the URL tree. This is useful for creating relative links between pages in different directories.
Table 18-8 shows the elements of the env array. These are defined during CGI requests, applicationdirect URL handlers, and page template processing:
AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE DOCUMENT_ROOT GATEWAY_INTERFACE HTTP_ACCEPT HTTP_AUTHORIZATION HTTP_COOKIE HTTP_FROM HTTP_REFERER HTTP_USER_AGENT PATH_INFO PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_USER REQUEST_METHOD REQUEST_URI SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
Authentication protocol (e.g., Basic). The size of the query data. The type of the query data. File system pathname of the document root. Protocol version, which is CGI/1.1. The Accept headers from the request. The Authorization challenge from the request. The cookie from the request. The From: header of the request. The Referer indicates the previous page. An ID string for the Web browser. Extra path information after the template file. The extra path information appended to the document root. The form query data. The client's IP address. The remote user name specified by Basic authentication. GET, POST, or HEAD. The complete URL that was requested. The name of the current file relative to the document root. The server name, e.g., www.beedub.com. The server's port, e.g., 80. The protocol (e.g., http or https). A software version string for the server.
Top
Table of Contents
Status
The /status URL is implemented in the status.tcl file. The status module implements the display of hit counts, document hits, and document misses (i.e., documents not found). The Status_Url command enables the application-direct URLs and assigns the top-level URL for the status module. The default configuration file contains this command: Status_Url /status Assuming this configuration, the following URLs are implemented:
Main status page showing summary counters and hit count histograms. Shows hit counts for each page. This page lets you sort by name or hit count, and limit files by patterns. A trivial URL that returns "hello". Shows miss counts for URLs that users tried to fetch. Displays an estimated size of Tcl code and Tcl data used by the TclHttpd program. This is a version of the main status page that doesn't use the graphical histograms of hit counts.
Debugging
The /debug URL is implemented in the debug.tcl file. The debug module has several useful URLs that let you examine variable values and other internal state. It is turned on with this command in the default configuration file: Debug_Url /debug Table 18-10 lists the /debug URLs. These URLs often require parameters that you can specify directly in the URL. For example, the /debug/echo URL echoes its query parameters:
http://yourserver:port/debug/echo?name=value&name2=val2
Lists the outstanding after events. Connect TclPro Debugger. This takes a host and port parameter. You need to install prodebug.tcl from TclPro into the server's script library directory. Echoes its query parameters. Accepts a title parameter. Displays the errorInfo variable along with the server's version number and Webmaster e-mail. Accepts title and errorInfo arguments. Displays a global array variable. The name of the variable is specified with the aname parameter. A more general value display function. The name of the variable is specified with the aname parameter. This can be a variable name, an array name, or a pattern that matches several variable names. Raises an error (to test error handling). Any parameters become the error string.
/debug/raise
The sample URL tree that is included in the distribution includes the file htdocs/hacks.html. This file has several small forms that use the /debug URLs to examine variables and source files. Example 18-15 shows the implementation of /debug/source. You can see that it limits the files to the main script library and to the script library associated with document templates. It may seem dangerous to have these facilities, but I reason that because my source directories are under my control, it cannot hurt to reload any source files. In general, the library scripts contain only procedure definitions and no global code that might reset state inappropriately. In practice, the ability to tune (i.e., fix bugs) in the
/debug/source
Sources a file from either the server's main library directory or the Doc_TemplateLibrary directory. The file is specified with the source parameter.
The sample URL tree that is included in the distribution includes the file htdocs/hacks.html. This file has several small forms that use the /debug URLs to examine variables and source files. Example 18-15 shows the implementation of /debug/source. You can see that it limits the files to the main script library and to the script library associated with document templates. It may seem dangerous to have these facilities, but I reason that because my source directories are under my control, it cannot hurt to reload any source files. In general, the library scripts contain only procedure definitions and no global code that might reset state inappropriately. In practice, the ability to tune (i.e., fix bugs) in the running server has proven useful to me on many occasions. It lets you evolve your application without restarting it! Example 18-15 The /debug/source application-direct URL implementation. proc Debug/source {source} { global Httpd Doc set source [file tail $source] set dirlist $Httpd(library) if {[info exists Doc(templateLibrary)]} { lappend dirlist $Doc(templateLibrary) } foreach dir $dirlist { set file [file join $dir $source] if [file exists $file] { break } } set error [catch {uplevel #0 [list source $file]} result] set html "<title>Source $source</title>\n" if {$error} { global errorInfo append html "<H1>Error in $source</H1>\n" append html "<pre>$result<p>$errorInfo</pre>" } else { append html "<H1>Reloaded $source</H1>\n" append html "<pre>$result</pre>" } return $html }
Administration
The /admin URL is implemented in the admin.tcl file. The admin module lets you load URL redirect tables, and it provides URLs that reset some of the counters maintained by the server. It is turned on with this command in the default configuration file:
Admin_Url /admin Currently, there is only one useful admin URL. The /admin/redirect/reload URL sources the file named redirect in the document root. This file is expected to contain a number of Url_Redirect commands that establish URL redirects. These are useful if you change the names of pages and want the old names to still work. The administration module has a limited set of application-direct URLs because the simple application-direct mechanism doesn't provide the right hooks to check authentication credentials. The HTML+Tcl templates work better with the authentication schemes.
Sending Email
The /mail URL is implemented in the mail.tcl file. The mail module implements various form handlers that email form data. Currently, it is UNIX-specific because it uses /usr/lib/sendmail to send the mail. It is turned on with this command in the default configuration file: Mail_Url /mail The application-direct URLs shown in Table 18-11 are useful form handlers. You can specify them as the ACTION parameter in your <FORM> tags. The mail module provides two Tcl procedures that are generally useful. The MailInner procedure is the one that sends mail. It is called like this: MailInner sendto subject from type body The sendto and from arguments are e-mail addresses. The type is the Mime type (e.g., text/plain or text/html) and appears in a Content-Type header. The body contains the mail message without any headers.
Sends e-mail with the errorInfo from a server error. It takes an email parameter for the destination address and an errorInfo parameter. Any additional arguments get included into the message. Sends e-mail containing form results. It requires these parameters: sendto for the destination address, subject for the mail subject, href and label for a link to display on the results page. Any additional arguments are formatted with the Tcl list command for easy processing by programs that read the mail. This is an older form of /mail/forminfo that doesn't format the data into Tcl lists. It requires only the email and subject parameters. The rest are formatted into the message body.
/mail/forminfo
/mail/formdata
The Mail_FormInfo procedure is designed for use in HTML+Tcl template files. It takes no arguments but instead looks in current query data for its parameters. It expects to find the same arguments as the /mail/forminfo direct URL. Using a template with Mail_FormInfo gives you more control over the result page than posting directly to /mail/forminfo, and is illustrated in Example 18-10 on page 257.
Top
Table of Contents
Quick Start
Unpack the tar file or the zip file, and you can run the server from the httpd.tcl script in the bin directory. On UNIX: tclsh httpd.tcl -port 80 This command will start the Web server on the standard port (80). By default it uses port 8015 instead. If you run it with the -help flag, it will tell you what command line options are available. If you use wish instead of tclsh, then a simple Tk user interface is displayed that shows how many hits the server is getting. On Windows you can double-click the httpd.tcl script to start the server. It will use wish and display the user interface. Again it will start on port 8015. You will need to create a shortcut that passes the port argument, or edit the associated configuration file to change this. Configuring the server is described later. Once you have the server running, you can connect to it from your Web browser. Use this URL if you are running on the default (nonstandard) port:
http://hostname:8015/
If you are running without a network connection, you may need to specify 127.0.0.1 for the hostname. This is the "localhost" address and will bypass the network subsystem.
http://127.0.0.1:8015/
This is a sample URL tree that demonstrates the features of the Web server. There is also some documentation there. One directory to note is htdocs/libtml, which is the standard place to put site-specific Tcl scripts used with the Tcl+HTML template facility.
src. There are a few C source files for a some optional packages. These have been precompiled for some platforms, and you can find the compiled libraries back under lib/Binaries in
platform-specific subdirectories.
Top
Table of Contents
Server Configuration
TclHttpd configures itself with three main steps. The first step is to process the command line arguments described in Table 18-12. The arguments are copied into the Config Tcl array. Anything not specified on the command line gets a default value. The next configuration step is to source the configuration file. The default configuration file is named tclhttpd.rc in the same directory as the start-up script (i.e., bin/tclhttpd.rc). This file can override command line arguments by setting the Config array itself. This file also has application-specific package require commands and other Tcl commands to initialize the application. Most of the Tcl commands used during initialization are described in the rest of this section. The final step is to actually start up the server. This is done back in the main httpd.tcl script. For example, to start the server for the document tree under /usr/local/htdocs and your own e-mail address as Webmaster, you can execute this command to start the server: tclsh httpd.tcl -docRoot /usr/local/htdocs -webmaster welch Alternatively, you can put these settings into a configuration file, and start the server with that configuration file: tclsh httpd.tcl -config mytclhttpd.rc In this case, the mytclhttpd.rc file could contain these commands to hard-wire the document root and Webmaster e-mail. In this case, the command line arguments cannot override these settings: set Config(docRoot) /usr/local/htdocs set Config(webmaster) welch
Table 18-12. The command line values are mapped into the Config array by the httpd.tcl start-up script.
Table 18-12. Basic TclHttpd Parameters. Parameter Port number. The default is 8015. Server name. The default is [info hostname]. IP address. The default is 0, for "any address". Directory of the root of the URL tree. The default is the htdocs directory. User ID of the TclHttpd process. The default is 50. (UNIX only.) Group ID of the TclHttpd process. The default is 100. (UNIX only.) Webmaster e-mail. The default is webmaster. Configuration file. The default is tclhttpd.rc. Additional directory to add to the auto_path. Command Option Config Variable
-port number -name name -ipaddr address -docRoot directory -uid uid -gid gid Config(port) Config(name) Config(ipaddr) Config(docRoot) Config(uid) Config(gid)
If the port number is 80, you can leave out the port specification. The call that starts the server using these parameters is found in httpd.tcl as: Httpd_Server $Config(name) $Config(port) $Config(ipaddr) Specifying the IP address is necessary only if you have several network interfaces (or several IP addresses assigned to one network interface) and want the server to listen to requests on a particular network address. Otherwise, by default, server accepts requests from any network interface.
user account. If you use Tcl+HTML templates that cache the results in HTML files, then you need to pick an account that can write those files. Otherwise, you may want to pick a very unprivileged account. The setuid function is available through the TclX (Extended Tcl) id command, or through a setuid extension distributed with TclHttpd under the src directory. If do not have either of these facilities available, then the attempt to change user ID gracefully fails. See the README file in the src directory for instructions on compiling and installing the extensions found there.
Webmaster Email
The Webmaster e-mail address is used for automatic error reporting in the case of server errors. This is defined in the configuration file with the following command: Doc_Webmaster $Config(webmaster) If you call Doc_Webmaster with no arguments, it returns the e-mail address you previously defined. This is useful when generating pages that contain mailto: URLs with the Webmaster address.
Document Root
The document root is the directory that contains the static files, templates, CGI scripts, and so on that make up your Web site. By default the httpd.tcl script uses the htdocs directory next to the directory containing httpd.tcl. It is worth noting the trick used to locate this directory: file join [file dirname [info script]] ../htdocs The info script command returns the full name of the http.tcl script, file dirname computes its directory, and file join finds the adjacent directory. The path ../htdocs works with file join on any platform. The default location of the configuration file is found in a similar way: file join [file dirname [info script]] tclhttpd.rc The configuration file initializes the document root with this call: Doc_Root $Config(docRoot) If you need to find out what the document root is, you can call Doc_Root with no arguments and it returns the directory of the document root. If you want to add additional document trees into your Web site, you can do that with a call like this in your configuration file:
Document Templates
The template mechanism has two main configuration options. The first specifies an additional library directory that contains your application-specific scripts. This lets you keep your application-specific files separate from the TclHttpd implementation. The command in the default configuration file specifies the libtml directory of the document tree: Doc_TemplateLibrary [file join $Config(docRoot) libtml]
You can also specify an alternate Tcl interpreter in which to process the templates. The default is to use the main interpreter, which is named {} according to the conventions described in Chapter 19. Doc_TemplateInterp {}
Log Files
The server keeps standard format log files. The Log_SetFile command defines the base name of the log file. The default configuration file uses this command: Log_SetFile /tmp/log$Config(port)_ By default the server rotates the log file each night at midnight. Each day's log file is suffixed with the current date (e.g., /tmp/logport_990218.) The error log, however, is not rotated, and all errors are accumulated in /tmp/logport_error. The log records are normally flushed every few minutes to eliminate an extra I/O operation on each HTTP transaction. You can set this period with Log_FlushMinutes. If minutes is 0, the log is flushed on every HTTP transaction. The default configuration file contains: Log_FlushMinutes 1
CGI Directories
You can register a directory that contains CGI programs with the Cgi_Directory command. This command has the interesting effect of forcing all files in the directory to be executed as CGI scripts, so you cannot put normal HTML files there. The default configuration file contains: Cgi_Directory /cgi-bin This means that the cgi-bin directory under the document root is a CGI directory. If you supply another argument to Cgi_Directory, then this is a file system directory that gets mapped into the URL defined by the first argument. You can also put CGI scripts into other directories and use the .cgi suffix to indicate that they should be executed as CGI scripts. The cgi.tcl file has some additional parameters that you can tune only by setting some elements of the Cgi Tcl array. See the comments in the beginning of that file for details.
Top
Table of Contents
Top
Table of Contents
Lists aliases that are defined in slave. Returns the target command and arguments for the alias cmd1 in slave. Defines cmd1 in slave that is an alias to cmd2 in master with additional args. Creates an interpreter named slave. Destroys interpreter slave. Evaluates cmd and args in slave. Returns 1 if slave is an interpreter, else 0. Exposes hidden command cmd in slave. Hides cmd from slave. Returns the commands hidden from slave. Invokes hidden command cmd and args in slave. Returns 1 if slave was created with -safe flag. Clears the issafe property of slave. Shares the I/O descriptor named file in master with
slave.
interp alias slave cmd1 master cmd2 arg ... interp create ?-safe? slave interp delete slave interp eval slave cmd args ... interp exists slave interp expose slave cmd interp hide slave cmd interp hidden slave interp invokehidden slave cmd arg ... interp issafe slave interp marktrusted slave interp share master file slave
interp slaves master interp target slave cmd interp transfer master file slave
Returns the list of slave interpreters of master. Returns the name of the interpreter that is the target of alias cmd in slave. Transfers the I/O descriptor named file from master to
slave.
Top
Table of Contents
Creating Interpreters
Here is a simple example that creates an interpreter, evaluates a couple of commands in it, and then deletes the interpreter: Example 19-1 Creating and deleting an interpreter. interp create foo => foo interp eval foo {set a 5} => 5 set sum [interp eval foo {expr $a + $a}] => 10 interp delete foo In Example 19-1 the interpreter is named foo. Two commands are evaluated in the foo interpreter: set a 5 expr $a + $a Note that curly braces are used to protect the commands from any interpretation by the main interpreter. The variable a is defined in the foo interpreter and does not conflict with variables in the main interpreter. The set of variables and procedures in each interpreter is completely independent.
interp create foo => foo interp eval foo {interp create bar} => bar interp create {foo bar2} => foo bar2 interp slaves => foo interp slaves foo => bar bar2 interp delete bar => interpreter named "bar" not found interp delete {foo bar} The example creates foo, and then it creates two children of foo. The first one is created by foo with this command: interp eval foo {interp create bar} The second child is created by the main interpreter. In this case, the grandchild must be named by a two-element list to indicate that it is a child of a child. The same naming convention is used when the grandchild is deleted: interp create {foo bar2} interp delete {foo bar2} The interp slaves operation returns the names of child (i.e., slave) interpreters. The names are relative to their parent, so the slaves of foo are reported simply as bar and bar2. The name for the current interpreter is the empty list, or {}. This is useful in command aliases and file sharing described later. For security reasons, it is not possible to name the master interpreter from within the slave.
foo eval {set a 5} interp eval foo {set a 5} And so are these: foo issafe interp issafe foo However, the operations delete, exists, share, slaves, target, and transfer cannot be used with the per interpreter command. In particular, there is no foo delete operation; you must use interp delete foo. If you have a deep hierarchy of interpreters, the command corresponding to the slave is defined only in the parent. For example, if a master creates foo, and foo creates bar, then the master must operate on bar with the interp command. There is no "foo bar " command defined in the master.
Top
Table of Contents
Safe Interpreters
A child can be created either safe (i.e., untrusted) or fully functional. In the examples so far, the children have been trusted and fully functional; they have all the basic Tcl commands available to them. An interpreter is made safe by eliminating certain commands. Table 19-2 lists the commands removed from safe interpreters. As described later, these commands can be used by the master on behalf of the safe interpreter. To create a safe interpreter, use the -safe flag: interp create -safe untrusted
Changes directory. Executes another program. Terminates the process. Sets modes of an I/O stream. Queries file attributes. Matches on file name patterns. Dynamically loads object code. Opens files and process pipelines. Determines the current directory. Opens network sockets. Loads scripts.
A safe interpreter does not have commands to manipulate the file system and other programs (e.g., cd, open, and exec). This ensures that untrusted scripts cannot harm the host computer. The socket
command is removed so that untrusted scripts cannot access the network. The exit, source, and load commands are removed so that an untrusted script cannot harm the hosting application. Note that commands like puts and gets are not removed. A safe interpreter can still do I/O, but it cannot create an I/O channel. We will show how to pass an I/O channel to a child interpreter on page 283. The initial state of a safe interpreter is very safe, but it is too limited. The only thing a safe interpreter can do is compute a string and return that value to the parent. By creating command aliases, a master can give a safe interpreter controlled access to resources. A security policy implements a set of command aliases that add controlled capabilities to a safe interpreter. We will show, for example, how to provide limited network and file system access to untrusted slaves. Tcl provides a framework to manage several security policies, which is described in Chapter 20.
Top
Table of Contents
Command Aliases
A command alias is a command in one interpreter that is implemented by a command in another interpreter. The master interpreter installs command aliases in its slaves. The command to create an alias has the following general form: interp alias slave cmd1 target cmd2 ?arg arg ...? This creates cmd1 in slave that is an alias for cmd2 in target. When cmd1 is invoked in slave, cmd2 is invoked in target. The alias mechanism is transparent to the slave. Whatever cmd2 returns, the slave sees as the return value of cmd1. If cmd2 raises an error, the error is propagated to the slave. Name the current interpreter with {}.
If target is the current interpreter, name it with {}. The empty list is the way to name yourself as the interpreter. This is the most common case, although target can be a different slave. The slave and target can even be the same interpreter. The arguments to cmd1 are passed to cmd2, after any additional arguments to cmd2 that were specified when the alias was created. These hidden arguments provide a safe way to pass extra arguments to an alias. For example, it is quite common to pass the name of the slave to the alias. In Example 19-3, exit in the interpreter foo is an alias that is implemented in the current interpreter (i.e., {} ). When the slave executes exit, the master executes: interp delete foo
Example 19-3 A command alias for exit. interp create foo interp alias foo exit {}interp delete foo interp eval foo exit # Child foo is gone.
Alias Introspection
You can query what aliases are defined for a child interpreter. The interp aliases command lists the aliases; the interp alias command can also return the value of an alias, and the interp target command tells you what interpreter implements an alias. These are illustrated in the following examples: Example 19-4 Querying aliases. proc Interp_ListAliases {name out} { puts $out "Aliases for $name" foreach alias [interp aliases $name] { puts $out [format "%-20s => (%s) %s" $alias \ [interp target $name $alias] \ [interp alias $name $alias]] } } Example 19-4 generates output in a human readable format. Example 19-5 generates the aliases as Tcl commands that can be used to re-create them later: Example 19-5 Dumping aliases as Tcl commands. proc Interp_DumpAliases {name out} { puts $out "# Aliases for $name" foreach alias [interp aliases $name] { puts $out [format "interp alias %s %s %s %s" \ $name $alias [list [interp target $name $alias]] \ [interp alias $name $alias]] } }
Top
Table of Contents
Hidden Commands
The commands listed in Table 19-2 are hidden instead of being completely removed. A hidden command can be invoked in a slave by its master. For example, a master can load Tcl scripts into a slave by using its hidden source command: interp create -safe slave interp invokehidden slave source filename Without hidden commands, the master has to do a bit more work to achieve the same thing. It must open and read the file and eval the contents of the file in the slave. File operations are described in Chapter 9. interp create -safe slave set in [open filename] interp eval slave [read $in] close $in Hidden commands were added in Tcl 7.7 in order to better support the Tcl/Tk browser plug-in described in Chapter 20. In some cases, hidden commands are strictly necessary; it is not possible to simulate them any other way. The best examples are in the context of Safe-Tk, where the master creates widgets or does potentially dangerous things on behalf of the slave. These will be discussed in more detail later. A master can hide and expose commands using the interp hide and interp expose operations, respectively. You can even hide Tcl procedures. However, the commands inside the procedure run with the same privilege as that of the slave. For example, if you are really paranoid, you might not want an untrusted interpreter to read the clock or get timing information. You can hide the clock and time commands: interp create -safe slave interp hide slave clock
interp hide slave time You can remove commands from the slave entirely like this: interp eval slave [list rename clock {}] interp eval slave [list rename time {}]
Top
Table of Contents
Substitutions
You must be aware of Tcl parsing and substitutions when commands are invoked in other interpreters. There are three cases corresponding to interp eval, interp invokehidden, and command aliases. With interp eval the command is subject to a complete round of parsing and substitutions in the target interpreter. This occurs after the parsing and substitutions for the interp eval command itself. In addition, if you pass several arguments to interp eval, those are concatenated before evaluation. This is similar to the way the eval command works as described in Chapter 19. The most reliable way to use interp eval is to construct a list to ensure the command is well structured: interp eval slave [list cmd arg1 arg2] With hidden commands, the command and arguments are taken directly from the arguments to interp invokehidden , and there are no substitutions done in the target interpreter. This means that the master has complete control over the command structure, and nothing funny can happen in the other interpreter. For this reason you should not create a list. If you do that, the whole list will be interpreted as the command name! Instead, just pass separate arguments to interp invokehidden and they are passed straight through to the target: interp invokehidden slave command arg1 arg2 Never eval alias arguments.
With aliases, all the parsing and substitutions occur in the slave before the alias is invoked in the master. The alias implementation should never eval or subst any values it gets from the slave to avoid executing arbitrary code.
For example, suppose there is an alias to open files. The alias does some checking and then invokes the hidden open command. An untrusted script might pass [exit] as the name of the file to open in order to create mischief. The untrusted code is hoping that the master will accidentally eval the filename and cause the application to exit. This attack has nothing to do with opening files; it just hopes for a poor alias implementation. Example 19-6 shows an alias that is not subject to this attack: Example 19-6 Substitutions and hidden commands. interp alias slave open {}safeopen slave proc safeopen {slave filename {mode r}} { # do some checks, then... interp invokehidden $slave open $filename $mode } interp eval slave {open \[exit\]} The command in the slave starts out as: open \[exit\] The master has to quote the brackets in its interp eval command or else the slave will try to invoke exit because of command substitution. Presumably exit isn't defined, or it is defined to terminate the slave. Once this quoting is done, the value of filename is [exit] and it is not subject to substitutions. It is safe to use $filename in the interp invokehidden command because it is only substituted once, in the master. The hidden open command also gets [exit] as its filename argument, which is never evaluated as a Tcl command.
Top
Table of Contents
In these commands, chanName exists in interp1 and is being shared or transferred to interp2. As with command aliases, if interp1 is the current interpreter, name it with {}. The following example creates a temporary file for an unsafe interpreter. The file is opened for reading and writing, and the slave can use it to store data temporarily. Example 19-7 Opening a file for an unsafe interpreter. proc TempfileAlias {slave} { set i 0 while {[file exists Temp$slave$i]} { incr i } set out [open Temp$slave$i w+] interp transfer {}$out $slave return $out } proc TempfileExitAlias {slave} { foreach file [glob -nocomplain Temp$slave*] { file delete -force $file } interp delete $slave } interp create -safe foo interp alias foo Tempfile {}TempfileAlias foo interp alias foo exit {}TempfileExitAlias foo The TempfileAlias procedure is invoked in the parent when the child interpreter invokes Tempfile. TempfileAlias returns the name of the open channel, which becomes the return value from Tempfile, so the child knows the name of the I/O channel. TempfileAlias uses interp transfer to pass the I/O channel to the child, so the child has permission to access the I/O channel. In this example, it would also work to invoke the hidden open command to create the I/O channel directly in the slave. Example 19-7 is not fully safe because the unsafe interpreter can still overflow the disk or create a million files. Because the parent has transferred the I/O channel to the child, it cannot easily monitor the I/O activity by the child. Example 19-9 addresses these issues.
Top
Table of Contents
Creates a safe interpreter and initialize the security policy mechanism. Initializes a safe interpreter so it can use security policies. Options are -accessPath pathlist, -nostatics, deleteHook script, -nestedLoadOk. Deletes a safe interpreter. Adds a directory to the slave's access path. Maps from a directory to the token visible in the slave for that directory. Sets or queries the logging command used by the safe base.
Table 19-4 lists the aliases defined in a safe interpreter by the safe base.
Loads scripts from directories in the access path. Loads binary extensions from the slaves access path. Only the dirname, join, extension, root, tail, pathname, and split operations are allowed. Destroys the slave interpreter.
Top
Table of Contents
Security Policies
A security policy defines what a safe interpreter can do. Designing security policies that are secure is difficult. If you design your own, make sure to have your colleagues review the code. Give out prizes to folks who can break your policy. Good policy implementations are proven with lots of review and trial attacks. The good news is that Safe-Tcl security policies can be implemented in relatively small amounts of Tcl code. This makes them easier to analyze and get correct. Here are a number of rules of thumb: Small policies are better than big, complex policies. If you do a lot of complex processing to allow or disallow access to resources, chances are there are holes in your policy. Keep it simple. Never eval arguments to aliases. If an alias accepts arguments that are passed by the slave, you must avoid being tricked into executing arbitrary Tcl code. The primary way to avoid this never is to eval arguments that are passed into an alias. Watch your expressions, too. The expr command does an extra round of substitutions, so brace all your expressions so that an attacker cannot pass [exit] where you expect a number! Security policies do not compose. Each time you add a new alias to a security policy, it changes the nature of the policy. Even if alias1 and alias2 are safe in isolation, there is no guarantee that they cannot be used together to mount an attack. Each addition to a security policy requires careful review.
# The index is a host name, and the # value is a list of port specifications, which can be # an exact port number # a lower bound on port number: N# a range of port numbers, inclusive: N-M array set safesock { sage.eng 3000-4000 www.sun.com 80 webcache.eng {80 8080} bisque.eng {80 1025-} } proc Safesock_PolicyInit {slave} { interp alias $slave socket {}SafesockAlias $slave } proc SafesockAlias {slave host port} { global safesock if ![info exists safesock($host)] { error "unknown host: $host" } foreach portspec $safesock($host) { set low [set high ""] if {[regexp {^([0-9]+)-([0-9]*)$}$portspec x low high]} { if {($low <= $port && $high == "") || ($low <= $port && $high >= $port)} { set good $port break } } elseif {$port == $portspec} { set good $port } } if [info exists good] { set sock [interp invokehidden $slave socket $host $good] interp invokehidden $slave fconfigure $sock \ -blocking 0 return $sock } error "bad port: $port" } The policy is initialized with Safesock_PolicyInit. The name of this procedure follows a naming convention used by the safe base. In this case, a single alias is installed. The alias gives the slave a socket command that is implemented by SafesockAlias in the master. The alias checks for a port that matches one of the port specifications for the host. If a match is found, then the invokehidden operation is used to invoke two commands in the slave. The socket command creates the network connection, and the fconfigure command puts the socket into nonblocking mode
so that read and gets by the slave do not block the application: set sock [interp invokehidden $slave socket $host $good] interp invokehidden $slave fconfigure $sock -blocking 0 The socket alias in the slave does not conflict with the hidden socket command. There are two distinct sets of commands, hidden and exposed. It is quite common for the alias implementation to invoke the hidden command after various permission checks are made. The Tcl Web browser plug-in ships with a slightly improved version of the Safesock policy. It adds an alias for fconfigure so that the http package can set end of line translations and buffering modes. The fconfigure alias does not let you change the blocking behavior of the socket. The policy has also been extended to classify hosts into trusted and untrusted hosts based on their address. A different table of allowed ports is used for the two classes of hosts. The classification is done with two tables: One table lists patterns that match trusted hosts, and the other table lists hosts that should not be trusted even though they match the first table. The improved version also lets a downloaded script connect to the Web server that it came from. The Web browser plug-in is described in Chapter 20.
proc Tempfile_PolicyInit {slave} { global tempfile interp alias $slave open {} \ TempfileOpenAlias $slave $tempfile(directory) \ $tempfile(maxfile) interp alias $slave puts {} TempfilePutsAlias $slave \ $tempfile(maxsize) interp alias $slave exit {} TempfileExitAlias $slave } proc TempfileOpenAlias {slave dir maxfile name {m r} {p 0777}} { global tempfile # remove sneaky characters regsub -all {|/:}[file tail $name] {}real set real [file join $dir $real] # Limit the number of files set files [glob -nocomplain [file join $dir *]] set N [llength $files] if {($N >= $maxfile) && (\ [lsearch -exact $files $real] < 0)} { error "permission denied" } if [catch {open $real $m $p}out] { return -code error "$name: permission denied" } lappend tempfile(channels,$slave) $out interp share {}$out $slave return $out } proc TempfileExitAlias {slave} { global tempfile interp delete $slave if [info exists tempfile(channels,$slave)] { foreach out $tempfile(channels,$slave) { catch {close $out} } unset tempfile(channels,$slave) } } # See also the puts alias in Example 22? on page 329 proc TempfilePutsAlias {slave max chan args} { # max is the file size limit, in bytes # chan is the I/O channel # args is either a single string argument, # or the -nonewline flag plus the string. if {[llength $args] > 2} { error "invalid arguments" } if {[llength $args] == 2} { if {![string match -n* [lindex $argv 0]]} { error "invalid arguments" }
set string [lindex $args 1] } else { set string [lindex $args 0]\n } set size [expr [tell $chan] + [string length $string]] if {$size > $max} { error "File size exceeded" } else { puts -nonewline $chan $string } } The TempfileAlias procedure is generalized in Example 19-9 to have parameters that specify the directory, name, and a limit to the number of files allowed. The directory and maxfile limit are part of the alias definition. Their existence is transparent to the slave. The slave specifies only the name and access mode (i.e., for reading or writing.) The Tempfile policy can be used by different slave interpreters with different parameters. The master is careful to restrict the files to the specified directory. It uses file tail to strip off any leading pathname components that the slave might specify. The tempfile(directory) definition is not shown in the example. The application must choose a directory when it creates the safe interpreter. The Browser security policy described on page 302 chooses a directory based on the name of the URL containing the untrusted script. The TempfilePutsAlias procedure implements a limited form of puts. It checks the size of the file with tell and measures the output string to see if the total exceeds the limit. The limit comes from a parameter defined when the alias is created. The file cannot grow past the limit, at least not by any action of the child interpreter. The args parameter is used to allow an optional -nonewline flag to puts. The value of args is checked explicitly instead of using the eval trick described in Example 103 on page 127. Never eval arguments to aliases or else a slave can attack you with arguments that contain embedded Tcl commands. The master and slave share the I/O channel. The name of the I/O channel is recorded in tempfile, and TempfileExitAlias uses this information to close the channel when the child interpreter is deleted. This is necessary because both parent and child have a reference to the channel when it is shared. The child's reference is automatically removed when the interpreter is deleted, but the parent must close its own reference. The shared I/O channel lets the master use puts and tell. It is also possible to implement this policy by using hidden puts and tell commands. The reason tell must be hidden is to prevent the slave from implementing its own version of tell that lies about the seek offset value. One advantage of using hidden commands is that there is no need to clean up the tempfile state about open channels. You can also layer the puts alias on top of any existing puts implementation. For example, a script may define puts to be a procedure that inserts data into a text widget. Example 19-10 shows the difference when using hidden commands. Example 19-10 Restricted puts using hidden commands. proc Tempfile_PolicyInit {slave} { global tempfile
interp alias $slave open {}\ TempfileOpenAlias $slave $tempfile(directory) \ $tempfile(maxfile) interp hide $slave tell interp alias $slave tell {}TempfileTellAlias $slave interp hide $slave puts interp alias $slave puts {}TempfilePutsAlias $slave \ $tempfile(maxsize) # no special exit alias required } proc TempfileOpenAlias {slave dir maxfile name {m r} {p 0777}} { # remove sneaky characters regsub -all {|/:}[file tail $name] {}real set real [file join $dir $real] # Limit the number of files set files [glob -nocomplain [file join $dir *]] set N [llength $files] if {($N >= $maxfile) && (\ [lsearch -exact $files $real] < 0)} { error "permission denied" } if [catch {interp invokehidden $slave \ open $real $m $p}out] { return -code error "$name: permission denied" } return $out } proc TempfileTellAlias {slave chan} { interp invokehidden $slave tell $chan } proc TempfilePutsAlias {slave max chan args} { if {[llength $args] > 2} { error "invalid arguments" } if {[llength $args] == 2} { if {![string match -n* [lindex $args 0]]} { error "invalid arguments" } set string [lindex $args 1] } else { set string [lindex $args 0]\n } set size [interp invokehidden $slave tell $chan] incr size [string length $string] if {$size > $max} { error "File size exceeded" } else { interp invokehidden $slave \ puts -nonewline $chan $string } }
unset after(id,$slave,$myid) after cancel $id } return "" } default { if {$argc == 1} { error "Usage: after time command args..." } if {[llength [array names after id,$slave,*]]\ >= $max} { error "Too many after events" } # Maintain concat semantics set command [concat [lrange $args 1 end]] # Compute our own id to pass the callback. set myid after#[incr after(id,$slave)] set id [after [lindex $args 0] \ [list SafeAfterCallback $slave $myid $command]] set after(id,$slave,$myid) $id return $myid } } } # SafeAfterCallback is the after callback in the master. # It evaluates its command in the safe interpreter. proc SafeAfterCallback {slave myid cmd} { global after unset after(id,$slave,$myid) if [catch { interp eval $slave $cmd } err] { catch {interp eval $slave bgerror $error} } } # SafeAfterExitAlias is an alias for exit that does cleanup. proc SafeAfterExitAlias {slave} { global after foreach id [array names after id,$slave,*] { after cancel $after($id) unset after($id) } interp delete $slave }
Top
Table of Contents
Top
Table of Contents
Tk in Child Interpreters
A child interpreter starts out with just the core Tcl commands. It does not include Tk or any other extensions that might be available to the parent interpreter. This is true whether or not the child interpreter is declared safe. You add extensions to child interpreters by using a form of the load command that specifies an interpreter: load {} Tk child Normally, load takes the name of the library file that contains the extension. In this case, the Tk package is a static package that is already linked into the program (e.g., wish or the plugin), so the file name is the empty string. The load command calls the Tk initialization procedure to register all the Tcl commands provided by Tk.
Embedding Tk Windows
By default, a slave interpreter that loads Tk gets a new top-level window. Wish supports a -use command line option that directs Tk to use an existing window as dot. You can use this to embed an application within another. For example, the following commands run a copy of Wish that uses the .embed toplevel as its main window: toplevel .embed exec wish -use [winfo id .embed] somescript.tcl & More often embedding it is used with child interpreters. If the interpreter is not safe, you can set the argv and argc variables in the slave before loading Tk: interp create trustedTk interp eval trustedTk \ [list set argv [list -use [winfo id .embed]]]
interp eval trustedTk [list set argc 2] load {}Tk trustedTk If the child interpreter is safe, then you cannot set argv and argc directly. The easiest way to pass -use to a safe interpreter is with the safe::loadTk command: safe::interpCreate safeTk safe::loadTk safeTk -use [winfo id .embed] When Tk is loaded into a safe interpreter it calls back into the master interpreter and evaluates the safe::TkInit procedure. The job of this procedure is to return the appropriate argv value for the slave. The safe::loadTk procedure stores its additional arguments in the safe::tkInit variable, and this value is retrieved by the safe::TkInit procedure and returned to the slave. This protocol is used so a safe interpreter cannot attempt to hijack the windows of its master by constructing its own argv variable!
Safe-Tk Restrictions
When Tk is loaded into a safe interpreter it hides several Tk commands. Primarily these are hidden to prevent denial of service attacks against the main process. For example, if a child interpreter did a global grab and never released it, all input would be forever directed to the child. Table 20-1 lists the Tk commands hidden by default from a safe interpreter. The Tcl commands that are hidden in safe interpreters are listed on page 279.
Ring the terminal bell. Access the CLIPBOARD selection. Direct input to a specified widget. Create and manipulate menus, because menus need grab. Manipulate the selection. Execute a command in another Tk application. Set the application name. Color choice dialog. File open dialog. File save dialog. Simple dialog boxes. Creates a detached window.
If you find these restrictions limiting, you can restore commands to safe interpreters with the interp expose command. For example, to get menus and toplevels working, you could do:
wm
If you find these restrictions limiting, you can restore commands to safe interpreters with the interp expose command. For example, to get menus and toplevels working, you could do: interp create -safe safeTk foreach cmd {grab menu menubutton toplevel wm} { interp expose safeTk $cmd } Instead of exposing the command directly, you can also construct aliases that provide a subset of the features. For example, you could disable the -global option to grab. Aliases are described in detail in Chapter 19. The Browser plugin defines a more elaborate configuration system to control what commands are available to slave interpreters. You can have lots of control, but you need to distribute the security policies that define what Tclets can do in the plugin. Configuring security policies for the plugin is described later.
Top
Table of Contents
Note that HTML tag parameters are case sensitive. Your Tclet may want to map all the parameter names to lowercase for convenience: foreach {name value}[array get embed_args] { set embed_args([string tolower $name]) $value } The plugin array has version, patchLevel, and release elements that identify the version and release date of the plugin implementation.
Example Plugins
The plugin home page is a great place to find Tclet examples. There are several plugins done by the Tcl/Tk team at Sunlabs, plus links to a wide variety of Tclets done on the Net. http://www.scriptics.com/plugin/ I wrote a cute little plugin that calculates the effective wheel diameter of multigear bicycles. Brian Lewis, who built the Tcl 8.0 byte-code compiler, explained to me the concept and how important this information is to bicycle enthusiasts. I put together a Tclet that displays the gear combinations on a Tk canvas and lets you change the number of gears and their size. You can find the result at: http://www.beedub.com/plugin/bike.html
The trust configuration. This defines what Tclets can use which security policies. This is in a config directory that is a peer of the Tcl script library. Local hooks. Local customization is supported by two hooks, siteInit and siteSafeInit. The siteInit procedure is called from the plugin when it first loads, and siteSafeInit is called when each applet is initialized. It is called with the name of the slave interpreter and the list of arguments from the <EMBED> tag. You can provide these as scripts that get loaded from the auto_path of the master interpreter. Chapter 12 describes how to manage script libraries found in the auto_path. The plugin also sources a personal start up script in which you can define siteInit and siteSafeInit . This script is ~/.pluginrc on UNIX and plugin/tclplugin.rc on Windows and Macintosh.
If this is defined and 1, then Tcl/Tk is loaded directly into the browser. Otherwise the plugin forks wish. This names the wish executable used to run Tclets. This must be version 8.0 or higher to properly support embedding. If this is set to 1, then a console is opened when a Tclet is loaded. The console prompts for Tcl commands that are evaluated in the master interpreter. If the value is something other than 1, then it is taken to be a script (e.g., TkCon) that implements a console. If 1, various status messages from the plugin are displayed. If defined, this file captures the log output.
TCL_PLUGIN_LOGWINDOW TCL_PLUGIN_LOGFILE
UNIX Configuration
Netscape looks in each user's ~/.netscape/plugins for the shared libraries that implement plugins. It also looks in a plugins directory under its main directory, which will vary from site to site. You can define a search path for plugins with the NXP_PLUGIN_PATH environment variable. The plugin script library is in ~/.tclplug/2.0/plugin. You can change this default location by setting the TCL_PLUGIN_DIR environment variable. Once the plugin finds its script library, it assumes the Tcl and Tk script directories, the security policies, and the trust map are in peer directories.
Windows Configuration
The default location for plugins is in the PLUGINS directory of the Netscape installation. The Tcl/Tk
plugin also works in Internet Explorer from the same location. The script libraries are found under C:\TCLPLUG\2.0. You can change this location by setting the registry variable: Software\Sun\Tcl Plugin\2.0\Directory
Macintosh Configuration
Installing the plugin on the Macintosh is a three-step process. In step one you unpack the initial download file, which creates another installer file. In step two you run that installer, which puts files into two locations. You specify one folder that will hold the documentation and the Netscape plugin. The rest of the plugin goes into a plugin folder under the Tool Command Language folder in your system's Extensions folder. It does not conflict with any existing Tcl/Tk installations you may already have. In step three, you complete the process by moving the Tcl Plugin file into the Plugins directory of your Netscape installation. The current version of the Macintosh plugin is limited to Netscape. Version 4 works better than Netscape 3, and Internet Explorer is not supported at all.
Top
Table of Contents
This provides a socket and fconfigure command that are limited to connecting to the host from which the Tclet was downloaded. You can specify an empty string for the host argument to socket to connect back to the home host. This policy also supports open and file delete that are similar to the Tempfile policy shown in Example 19-9 on page 288. This provides limited local storage that is inside a directory that is, by default, private to the Tclet. Files in the private directory persist after the Tclet exits, so it can maintain long term state. Tclets from the same server can share the directory by putting the same prefix=partialurl argument in their EMBED tag. The partialurl must be a prefix of the Tclet's URL. Finally, the home policy automatically provides a browser package that is described later.
inside.
This is just like the home policy, except the site administrator controls a table of hosts and ports to which untrusted slaves can connect with socket. A similar set of tables control what URLs can be accessed with the browser package. This is similar to the Safesock policy is shown in Example 19-8 on page 286. The set of hosts is supposed to be inside the firewall. The local file storage used by this policy is distinct from that used by the home and outside policies. This is true even if Tclets try to share by using the prefix=partialurl parameter.
outside .
This is just like the home and inside policies, except that the set of hosts is configured to be outside the firewall. The local file storage used by this policy is distinct from that used by the home and inside policies.
trusted . This policy restores all features of Tcl and Tk. This policy lets you launch all your Tcl
and Tk applications from the Web browser. The default trust map settings do not allow this for any Tclet. The trust map configuration is described later.
javascript. This policy provides a superset of the browser
package that lets you invoke arbitrary Javascript and to write HTML directly to frames. This does not have the limited socket or temporary file access that the home, inside, and outside policies have. However, the javascript policy places no restrictions on the URLs you can fetch, plus it lets Tclets execute Javascript, which may have its own security risks. The default trust map settings do not allow this for any Tclet.
Display string in the browser status window. Fetches url, if allowed by the security policy. The callbacks occur before, during, and after the url data is returned. Causes the browser to display url in frame. Posts data to url. The callbacks are the same as for browser::getURL. If raw is 0, then data is a name value list that gets encoded automatically. Otherwise it is assume to be encoded already. Posts data to url and displays the result in frame. The raw argument is the same as in browser::getForm.
The browser::getURL function uses the browser's built-in functions, so it understands proxies and supports ftp:, http:, and file: urls. Unfortunately, the browser::getURL interface is different than the http::geturl interface. It uses a more complex callback scheme that is due to the nature of the browser's built-in functions. If you do not specify any callbacks, then the call blocks until all the data is received, and then that data is returned. The callback functions are described in Table 20-4.
This is called when data starts to arrive from url. The name identifies the requesting Tclet, and the stream identifies the connection. The mimetype, datemodified , and size parameters are attributes of the returned data. This is called when size bytes of data arrive for Tcllet name over stream. This is called when the request has completed, although there may be some final bytes in data. The reason is one of: EOF, NETWOR_ERROR, USER_BREAK, or TIMEOUT .
Top
Table of Contents
Remember that the configuration files affect the client machine, which is the workstation that runs the Web browser. If you create Tclets that require custom security policies, you have the burden of distributing the configuration files to clients that will use your Tclets. You also have the burden of convincing them your security policy is safe!
disallow outside disallow trusted allow javascript ifallowed trustedJavaScriptURLS \ $originURL This configuration grants all Tclets the right to use the home policy, disallows all Tclets from using the intercom, inside, outside , and trusted policies, and grants limited access to the javascript policy. If you are curious, the config files are almost Tcl, but not quite. I lost an argument about that one, so these are stylized configuration files that follow their own rules. For example, the originURL variable is not defined in the config file, but is a value that is tested later when the Tclet is loaded. I'll just give examples here and you can peer under the covers if you want to learn how they are parsed. The ifallowed clause depends on another section to describe the trust mapping for that policy. For the javascript policy, the config/plugin.cfg file contains: section trustedJavascriptURLs allow http://sunscript.sun.com:80/plugin/javascript/* Unfortunately this server isn't running anymore, so you may want to add the Scriptics web server to your own configuration: allow http://www.scriptics.com:80/plugin/javascript/* You can use a combination of allow and disallow rules in a section. The arguments to allow and disallow are URL string match patterns, and they are processed in order. For example, you could put a liberal allow rule followed by disallow rules that restrict access, or vice versa. It is probably safest to explicitly list each server that you trust.
The aliases that make up a security policy are organized into sets called features. The features are listed in the main config/plugin.cfg configuration file: variable featuresList {url stream network persist unsafe} In turn, each security policy configuration file lists what features are part of the policy. For example, the config/home.cfg file lists these features: section features allow url allow network allow persist unless {[string match {UNKNOWN *}\ [getattr originURL]]} Each feature is implemented in a file in the safetcl directory of the distribution. For example, the url feature is implemented in safetcl/url.tcl. The code in these files follows some conventions in order to work with the configuration mechanism. Each one is implemented inside a namespace that is a child of the safefeature namespace (e.g., safefeature::url). It must implement an install procedure that is called to initialize the feature for a new Tclet. It is inside this procedure that the various allow/disallow rules are checked. The cfg::allowed command supports the rule language used in the .cfg files.
Top
Table of Contents
Top
Table of Contents
specialized behaviors, you use the bind command to register a Tcl command that runs when an event occurs. There are lots of events, including mouse motion, keystrokes, window resize, and window destruction. You can also define virtual events, like Cut and Paste, that are caused by different events on different platforms. Bindings are discussed in detail in Chapter 26. Chapter 16 describes I/O events and the Tcl event loop, while Chapter 47 describes C programming and the event loop. Event bindings are grouped into classes, which are called bindtags. The bindtags command associates a widget with an ordered set of bindtags. The level of indirection between the event bindings and the widgets creates a flexible and powerful system for managing events. You can create your own bindtags and dynamically change the bindtags for a widget to support mode changes in your application. A concept related to binding is focus. At any given time, one of the widgets has the input focus, and keyboard events are directed to it. There are two general approaches to focusing: give focus to the widget under the mouse, or explicitly set the focus to a particular widget. Tk provides commands to change focus so you can implement either style of focus management. To support modal dialog boxes, you can forcibly grab the focus away from other widgets. Chapter 36 describes focus, grabs, and dialogs. The basic structure of a Tk script begins by creating widgets and arranging them with a geometry manager, and then binding actions to the widgets. After the interpreter processes the commands that initialize the user interface, the event loop is entered and your application begins running. If you use wish interactively, it creates and displays an empty main window and gives you a commandline prompt. With this interface, your keyboard commands are handled by the event loop, so you can build your Tk interface gradually. As we will see, you will be able to change virtually all aspects of your application interactively.
Top
Table of Contents
Hello, World! in Tk
Our first Tk script is very simple. It creates a button that prints "Hello, World!" to standard output when you press it. Above the button widget is a title bar that is provided by the window manager, which in this case is twm under X windows: Example 21-1 "Hello, World!" Tk program.
#!/usr/local/bin/wish button .hello -text Hello \ -command {puts stdout "Hello, World!"} pack .hello -padx 20 -pady 10 The first line identifies the interpreter for the script: #!/usr/local/bin/wish This special line is necessary if the script is in a file that will be used like other UNIX command files. Chapter 2 describes how to set up scripts on different platforms. There are two Tcl commands in the script: one to create the button, and one to make it visible on the display. The button command creates an instance of a button:
button .hello -text Hello \ -command {puts stdout "Hello, World!"} => .hello The name of the button is .hello. The label on the button is Hello, and the command associated with the button is: puts stdout "Hello, World!" The pack command maps the button onto the screen. Some padding parameters are supplied, so there is space around the button: pack .hello -padx 20 -pady 10 If you type these two commands into wish, you will not see anything happen when the button command is given. After the pack command, though, you will see the empty main window shrink to be just big enough to contain the button and its padding. The behavior of the packer will be discussed further in Chapters 22 and 23. Tk uses an object-based system for creating and naming widgets. Associated with each class of widget (e.g., Button) is a command that creates instances of that class of widget. As the widget is created, a new Tcl command is defined that operates on that instance of the widget. Example 21-1 creates a button named .hello, and we can operate on the button using its name as a Tcl command. For example, we can cause the button to highlight a few times: .hello flash Or we can run the command associated with the button: .hello invoke => Hello, World! Tk has widget classes and instances, but it is not fully object oriented. It is not possible to subclass a widget class and use inheritance. Instead, Tk provides very flexible widgets that can be configured in many different ways to tune their appearance. The resource database can store configuration information that is shared by many widgets, and new classes can be introduced to group resources. Widget behavior is shared by using binding tags that group bindings. Instead of building class hierarchies, Tk uses composition to assemble widgets with shared behavior and attributes.
Top
Table of Contents
Naming Tk Widgets
The period in the name of the button instance, .hello, is required. Tk uses a naming system for the widgets that reflects their position in a hierarchy of widgets. The root of the hierarchy is the main window of the application, and its name is simply a dot (i.e., .). This is similar to the naming convention for directories in UNIX where the root directory is named /, and then / is used to separate components of a file name. Tk uses a dot in the same way. Each widget that is a child of the main window is named something like .foo. A child widget of .foo would be .foo.bar, and so on. Just as file systems have directories that are containers for files and other directories, the Tk window hierarchy uses frame widgets that are containers for widgets and other frames. Each component of a Tk pathname must start with a lowercase letter or a number. Obviously, a component cannot include a period, either. The lower case restriction avoids a conflict with resource class names that begin with an upper case letter. A resource name can include Tk pathname components and Tk widget classes, and case is used to distinguish them. Chapter 28 describes resources in detail. Store widget names in variables.
There is one drawback to the Tk widget naming system. If your interface changes enough it can result in some widgets changing their position in the widget hierarchy. In that case they may need to change their name. You can insulate yourself from this programming nuisance by using variables to hold the names of important widgets. Use a variable reference instead of widget pathnames in case you need to change things, or if you want to reuse your code in a different interface.
Top
Table of Contents
Configuring Tk Widgets
Example 21-1 illustrates a style of named parameter passing that is prevalent in the Tk commands. Pairs of arguments specify the attributes of a widget. The attribute names begin with -, such as -text, and the next argument is the value of that attribute. Even the simplest Tk widget can have a dozen or more attributes that can be specified this way, and complex widgets can have 20 or more attributes. However, the beauty of Tk is that you need to specify only the attributes for which the default value is not good enough. This is illustrated by the simplicity of the Hello, World example. Finally, each widget instance supports a configure operation, which can be abbreviated to config, that can query and change these attributes. The syntax for config uses the same named argument pairs used when you create the widget. For example, we can change the background color of the button to red even after it has been created and mapped onto the screen: .hello config -background red Widget attributes can be redefined any time, even the text and command that were set when the button was created. The following command changes .hello into a goodbye button: .hello config -text Goodbye! -command exit Widgets have a cget operation to query the current value of an attribute: .hello cget -background => red You can find out more details about a widget attribute by using configure without a value: .hello config -background => -background background Background #ffe4c4 red
The returned information includes the command-line switch, the resource name, the class name, the default value, and the current value, which is last. The class and resource name have to do with the resource mechanism described in Chapter 28. If you only specify configure and no attribute, then a list of the configuration information for all widget attributes is returned. Example 21-2 uses this to print out all the information about a widget: Example 21-2 Looking at all widget attributes. proc Widget_Attributes {w {out stdout}} { puts $out [format "%-20s %-10s %s" Attribute Default Value] foreach item [$w configure] { puts $out [format "%-20s %-10s %s" \ [lindex $item 0] [lindex $item 3] \ [lindex $item 4]] } }
Top
Table of Contents
You need to know these naming conventions if you specify widget attributes via the resource mechanism. The command-line option can be derived from the resource name by mapping it to all lowercase. The primary advantage of using resources to specify attributes is that you do not have to litter your code with attribute specifications. With just a few resource database entries you can specify attributes for all your widgets. In addition, if attributes are specified with resources, users can provide alternate resource specifications in order to override the values supplied by the application. For attributes like colors and fonts, this feature can be important to users. Resource specifications are described in detail in Chapter 28.
The tkman program provides a very nice graphical user interface to the UNIX manual pages. On the Macintosh platform, the manual pages are formatted into HTML documents that you can find in the HTML Docs folder of the Tcl/Tk distribution. On Windows, the manual pages are formatted into Help documents. You can find the manual pages on the web at: http://www.scriptics.com/man/ There are a large number of attributes that are common across most of the Tk widgets. These are described in a separate man page under the name options. Each man page begins with a STANDARD OPTIONS section that lists which of these standard attributes apply, but you have to look at the options man page for the description. In contrast, the tables in this book always list all widget attributes.
Top
Table of Contents
Pg. Description 388 Create a command button. 392 Create a toggle button that is linked to a Tcl variable. 392 Create one of a set of radio buttons linked to one variable. 396 Create a button that posts a menu. 396 Create a menu. 475 Create a canvas, which supports lines, boxes, bitmaps, images, arcs, text, polygons, and embedded widgets. 420 Create a read-only, one-line text label. 437 Create a one-line text entry widget. 422 Create a read-only, multiline text message. 443 Create a line-oriented, scrolling text widget. 453 Create a general-purpose, editable text widget. 429 Create a scrollbar that can be linked to another widget.
Pg. Description 425 Create a scale widget that adjusts the value of a variable. 417 Create a container widget used with geometry managers. 417 Create a frame that is a new top level window. Table 21-2. Tk widget-manipulation commands.
Command bell bind bindtags clipboard destroy event focus font grab grid image lower option pack place raise selection send tk tkerror tkwait update winfo wm
Pg. Description 428 Ring the terminal bell device. 369 Bind a Tcl command to an event. 371 Create binding classes and control binding inheritance. 510 Manipulate the clipboard. 521 Delete a widget. 380 Define and generate virtual events. 518 Control the input focus. 555 Set and query font attributes and measurements. 520 Steal the input focus from other widgets. 358 Arrange widgets into a grid with constraints. 542 Create and manipulate images. 349 Lower a window in the stacking order. 409 Set and query the resources database. 348 Pack a widget in the display with constraints. 367 Place a widget in the display with positions. 349 Raise a window in the stacking order. 509 Manipulate the selection. 562 Send a Tcl command to another Tk application. 582 Query or set the application name. 190 Handler for background errors. 520 Wait for an event. 524 Update the display by going through the event loop. 576 Query window state. 571 Interact with the window manager.
Pg. Description 537 Install bisque family of colors. 517 Dialog to select a color. (Tk 4.2) 515 Create simple dialogs. 518 Install mouse-tracking focus model. 519 Focus on next widget in tab order. 519 Focus on previous widget in tab order. 516 Dialog to open an existing file. (Tk 4.2) 516 Dialog to open a new file. (Tk 4.2) 516 Message dialog. (Tk 4.2) 398 Create an option menu. 398 Create a pop-up menu. 537 Set the standard color palette. (Tk 4.2)
Top
Table of Contents
Top
Table of Contents
ExecLog
Our first example provides a simple user interface to running another program with the exec command. The interface consists of two buttons, Run it and Quit, an entry widget in which to enter a command, and a text widget in which to log the results of running the program. The script runs the program in a pipeline and uses the fileevent command to wait for output. This structure lets the user interface remain responsive while the program executes. You could use this to run make, for example, and it would save the results in the log. The complete example is given first, and then its commands are discussed in more detail. Example 22-1 Logging the output of a program run with exec.
#!/usr/local/bin/wish # execlog - run a program with exec and log the output # Set window title wm title . ExecLog # Create a frame for buttons and entry.
frame .top -borderwidth 10 pack .top -side top -fill x # Create the command buttons. button .top.quit -text Quit -command exit set but [button .top.run -text "Run it" -command Run] pack .top.quit .top.run -side right # Create a labeled entry for the command label .top.l -text Command: -padx 0 entry .top.cmd -width 20 -relief sunken \ -textvariable command pack .top.l -side left pack .top.cmd -side left -fill x -expand true # Set up key binding equivalents to the buttons bind .top.cmd <Return> Run bind .top.cmd <Control-c> Stop focus .top.cmd # Create a text widget to log the output frame .t set log [text .t.log -width 80 -height 10 \ -borderwidth 2 -relief raised -setgrid true \ -yscrollcommand {.t.scroll set}] scrollbar .t.scroll -command {.t.log yview} pack .t.scroll -side right -fill y pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true # Run the program and arrange to read its input proc Run {} { global command input log but if [catch {open "|$command |& cat"}input] { $log insert end $input\n } else { fileevent $input readable Log $log insert end $command\n $but config -text Stop -command Stop } } # Read and log output from the program proc Log {} {
global input log if [eof $input] { Stop } else { gets $input line $log insert end $line\n $log see end } } # Stop the program and fix up the button proc Stop {} { global input but catch {close $input} $but config -text "Run it" -command Run }
Window Title
The first command sets the title that appears in the title bar implemented by the window manager. Recall that dot (i.e., .) is the name of the main window: wm title . ExecLog The wm command communicates with the window manager. The window manager is the program that lets you open, close, and resize windows. It implements the title bar for the window and probably some small buttons to close or resize the window. Different window managers have a distinctive look; the figure shows a title bar from twm, a window manager for X.
Command Buttons
Two buttons are created: one to run the command, the other to quit the program. Their names, .top.quit and .top.run, imply that they are children of the .top frame. This affects the pack command, which positions widgets inside their parent by default: button .top.quit -text Quit -command exit set but [button .top.run -text "Run it" \ -command Run] pack .top.quit .top.run -side right
The pipeline diverts error output from the command through the cat program. If you do not use cat like this, then the error output from the pipeline, if any, shows up as an error message when the pipeline is closed. In this example it turns out to be awkward to distinguish between errors generated from the program and errors generated because of the way the Stop procedure is implemented. Furthermore, some programs interleave output and error output, and you might want to see the error output in order instead of all at the end. If the pipeline is opened successfully, then a callback is set up using the fileevent command. Whenever the pipeline generates output, then the script can read data from it. The Log procedure is registered to be called whenever the pipeline is readable: fileevent $input readable Log The command (or the error message) is inserted into the log. This is done using the name of the text widget, which is stored in the log variable, as a Tcl command. The value of the command is appended to the log, and a newline is added so that its output will appear on the next line. $log insert end $command\n The text widget's insert function takes two parameters: a mark and a string to insert at that mark. The symbolic mark end represents the end of the contents of the text widget. The run button is changed into a stop button after the program begins. This avoids a cluttered interface and demonstrates the dynamic nature of a Tk interface. Again, because this button is used in a few different places in the script, its pathname has been stored in the variable but: $but config -text Stop -command Stop
Cross-Platform Issues
This script will run on UNIX and Windows, but not on Macintosh because there is no exec command. One other problem is the binding for <Control-c> to cancel the job. This is UNIX-like, while Windows users might expect <Escape> to cancel a job, and Macintosh users expect <Command-.>. Platform_CancelEvent defines a virtual event, <<Cancel>>, and Stop is bound to it: Example 22-2 A platform-specific cancel event. proc Platform_CancelEvent {} { global tcl_platform switch $tcl_platform(platform) { unix { event add <<Cancel>> <Control-c> } windows { event add <<Cancel>> <Escape> } macintosh { event add <<Cancel>> <Command-.> }
} } bind .top.entry <<Cancel>> Stop There are other virtual events already defined by Tk. The event command and virtual events are described on page 380.
Top
Table of Contents
# is defined by the Tcl shell in Example 22? on page 329 button $f.load -text Run -command Run button $f.reset -text Reset -command Reset pack $f.quit $f.reset $f.load $f.next $f.prev -side right # A label identifies the current example label $f.label -textvariable browse(current) pack $f.label -side right -fill x -expand true # Create the menubutton and menu menubutton $f.ex -text Examples -menu $f.ex.m pack $f.ex -side left set m [menu $f.ex.m] # Create the text to display the example # Scrolled_Text is defined in Example 30? on page 430 set browse(text) [Scrolled_Text .body \ -width 80 -height 10\ -setgrid true] pack .body -fill both -expand true # Look through the example files for their ID number. foreach f [lsort -dictionary [glob [file join $browse(dir) *]]] { if [catch {open $f}in] { puts stderr "Cannot open $f: $in" continue } while {[gets $in line] >= 0} { if [regexp {^# Example ([0-9]+)-([0-9]+)}$line \ x chap ex] { lappend examples($chap) $ex lappend browse(list) $f # Read example title gets $in line set title($chap-$ex) [string trim $line "# "] set file($chap-$ex) $f close $in break } } } # Create two levels of cascaded menus. # The first level divides up the chapters into chunks. # The second level has an entry for each example. option add *Menu.tearOff 0
set limit 8 set c 0; set i 0 foreach chap [lsort -integer [array names examples]] { if {$i == 0} { $m add cascade -label "Chapter $chap..." \ -menu $m.$c set sub1 [menu $m.$c] incr c } set i [expr ($i +1) % $limit] $sub1 add cascade -label "Chapter $chap" -menu $sub1.sub$i set sub2 [menu $sub1.sub$i ] foreach ex [lsort -integer $examples($chap)] { $sub2 add command -label "$chap-$ex $title($chap-$ex)" \ -command [list Browse $file($chap-$ex)] } } # Display a specified file. The label is updated to # reflect what is displayed, and the text is left # in a read-only mode after the example is inserted. proc Browse { file } { global browse set browse(current) [file tail $file] set browse(curix) [lsearch $browse(list) $file] set t $browse(text) $t config -state normal $t delete 1.0 end if [catch {open $file}in] { $t insert end $in } else { $t insert end [read $in] close $in } $t config -state disabled } # Browse the next and previous files in the list set browse(curix) -1 proc Next {} { global browse if {$browse(curix) < [llength $browse(list)] - 1} { incr browse(curix) } Browse [lindex $browse(list) $browse(curix)] } proc Previous {} { global browse if {$browse(curix) > 0} {
incr browse(curix) -1 } Browse [lindex $browse(list) $browse(curix)] } # Run the example in the shell proc Run {} { global browse EvalEcho [list source \ [file join $browse(dir) $browse(current)]] } # Reset the slave in the eval server proc Reset {} { EvalEcho reset }
variable is used to select a different example directory on different platforms. You may need to edit the on-line example to match your system. The example uses glob to find all the files in the exsource directory. The file join command is used to create the file name pattern in a platform-independent way. The result of glob is sorted explicitly so the menu entries are in the right order. Each file is read one line at a time with gets, and then regexp is used to scan for keywords. The loop is repeated here for reference: foreach f [lsort -dictionary [glob [file join $browse(dir) *]]] { if [catch {open $f}in] { puts stderr "Cannot open $f: $in" continue } while {[gets $in line] >= 0} { if [regexp {^# Example ([0-9]+)-([0-9]+)}$line \ x chap ex] { lappend examples($chap) $ex lappend browse(list) $f # Read example title gets $in line set title($chap-$ex) [string trim $line "# "] set file($chap-$ex) $f close $in break } } } The example files contain lines like this: # Example 1-1 # The Hello, World! program The regexp picks out the example numbers with the ([0-9]+)-([0-9]+) part of the pattern, and these are assigned to the chap and ex variables. The x variable is assigned the value of the whole match, which is more than we are interested in. Once the example number is found, the next line is read to get the description of the example. At the end of the foreach loop the examples array has an element defined for each chapter, and the value of each element is a list of the examples for that chapter.
Cascaded Menus
The values in the examples array are used to build up a cascaded menu structure. First a menubutton is created that will post the main menu. It is associated with the main menu with its menu attribute. The menu must be a child of the menubutton for its display to work properly: menubutton $f.ex -text Examples -menu $f.ex.m set m [menu $f.ex.m]
There are too many chapters to put them all into one menu. The main menu has a cascade entry for each group of eight chapters. Each of these submenus has a cascade entry for each chapter in the group, and each chapter has a menu of all its examples. Once again, the submenus are defined as a child of their parent menu. Note the inconsistency between menu entries and buttons. Their text is defined with the -label option, not -text. Other than this they are much like buttons. Chapter 27 describes menus in more detail. The code is repeated here: set limit 8 ; set c 0 ; set i 0 foreach key [lsort -integer [array names examples]] { if {$i == 0} { $m add cascade -label "Chapter $key..." \ -menu $m.$c set sub1 [menu $m.$c] incr c } set i [expr ($i +1) % $limit] $sub1 add cascade -label "Chapter $key" -menu $sub1.sub$i set sub2 [menu $sub1.sub$i] foreach ex [lsort -integer $examples($key)] { $sub2 add command -label "$key-$ex $title($key-$ex)" \ -command [list Browse $file($key-$ex)] } }
Top
Table of Contents
A Tcl Shell
This section demonstrates the text widget with a simple Tcl shell application. It uses a text widget to prompt for commands and display their results. It uses a second Tcl interpreter to evaluate the commands you type. This dual interpreter structure is used by the console built into the Windows and Macintosh versions of wish. The TkCon application written by Jeff Hobbs is an even more elaborate console that has many features to support interactive Tcl use. Example 22-4 is written to be used with the browser from Example 22-3 in the same application. The browser's Run button runs the current example in the shell. An alternative is to have the shell run as a separate process and use the send command to communicate Tcl commands between separate applications. That alternative is shown in Example 40-2 on page 565. Example 22-4 A Tcl shell in a text widget. #!/usr/local/bin/wish # Simple evaluator. It executes Tcl in a slave interpreter set t [Scrolled_Text .eval -width 80 -height 10] pack .eval -fill both -expand true # Text tags give script output, command errors, command # results, and the prompt a different appearance $t $t $t $t tag tag tag tag configure configure configure configure prompt -underline true result -foreground purple error -foreground red output -foreground blue
# Insert the prompt and initialize the limit mark set eval(prompt) "tcl> " $t insert insert $eval(prompt) prompt $t mark set limit insert $t mark gravity limit left
focus $t set eval(text) $t # Key bindings that limit input and eval things. The break in # the bindings skips the default Text binding for the event. bind $t <Return> {EvalTypein ; break} bind $t <BackSpace> { if {[%W tag nextrange sel 1.0 end] != ""} { %W delete sel.first sel.last } elseif {[%W compare insert > limit]} { %W delete insert-1c %W see insert } break } bind $t <Key> { if [%W compare insert < limit] { %W mark set insert end } } # Evaluate everything between limit and end as a Tcl command proc EvalTypein {} { global eval $eval(text) insert insert \n set command [$eval(text) get limit end] if [info complete $command] { $eval(text) mark set limit insert Eval $command } } # Echo the command and evaluate it proc EvalEcho {command} { global eval $eval(text) mark set insert end $eval(text) insert insert $command\n Eval $command } # Evaluate a command and display its result proc Eval {command} { global eval $eval(text) mark set insert end if [catch {$eval(slave) eval $command}result] { $eval(text) insert insert $result error } else {
$eval(text) insert insert $result result } if {[$eval(text) compare insert != "insert linestart"]} { $eval(text) insert insert \n } $eval(text) insert insert $eval(prompt) prompt $eval(text) see insert $eval(text) mark set limit insert return } # Create and initialize the slave interpreter proc SlaveInit {slave} { interp create $slave load {}Tk $slave interp alias $slave reset {}ResetAlias $slave interp alias $slave puts {}PutsAlias $slave return $slave } # The reset alias deletes the slave and starts a new one proc ResetAlias {slave} { interp delete $slave SlaveInit $slave } # The puts alias puts stdout and stderr into the text widget proc PutsAlias {slave args} { if {[llength $args] > 3} { error "invalid arguments" } set newline "\n" if {[string match "-nonewline" [lindex $args 0]]} { set newline "" set args [lreplace $args 0 0] } if {[llength $args] == 1} { set chan stdout set string [lindex $args 0]$newline } else { set chan [lindex $args 0] set string [lindex $args 1]$newline } if [regexp (stdout|stderr) $chan] { global eval $eval(text) mark gravity limit right $eval(text) insert limit $string output $eval(text) see limit
$eval(text) mark gravity limit left } else { puts -nonewline $chan $string } } set eval(slave) [SlaveInit shell]
Multiple Interpreters
The SlaveInit procedure creates another interpreter to evaluate the commands. This prevents conflicts with the procedures and variables used to implement the shell. Initially, the slave interpreter only has access to Tcl commands. The load command installs the Tk commands, and it creates a new top-level window that is "." for the slave interpreter. Chapter 20 describes how you can embed the window of the slave within other frames. The shell interpreter is not created with the -safe flag, so it can do anything. For example, if you type exit, it will exit the whole application. The SlaveInit procedure installs an alias, reset, that just deletes the slave interpreter and creates a new one. You can use this to clean up after working in the shell for a while. Chapter 19 describes the interp command in detail.
Top
Table of Contents
Top
Table of Contents
# Make the main window black . config -bg black # Create and pack two frames frame .one -width 40 -height 40 -bg white frame .two -width 100 -height 50 -bg grey50 pack .one .two -side top
frame .one -width 40 -height 40 -bg white frame .two -width 100 -height 50 -bg grey50 pack propagate . false pack .one .two -side top
Top
Table of Contents
frame .one -bg white frame .two -width 100 -height 50 -bg grey50 # Create a row of buttons foreach b {alpha beta gamma} { button .one.$b -text $b pack .one.$b -side left } pack .one .two -side top
frame .one -bg white frame .two -width 100 -height 50 -bg grey50 foreach b {alpha beta} { button .one.$b -text $b pack .one.$b -side left } # Create a frame for two more buttons frame .one.right foreach b {delta epsilon} { button .one.right.$b -text $b pack .one.right.$b -side bottom } pack .one.right -side right pack .one .two -side top You can build more complex arrangements by introducing nested frames and switching between horizontal and vertical stacking as you go. Within each frame pack all the children with either a combination of -side left and -side right, or -side top and -side bottom. Example 23-4 replaces the .one.gamma button with a vertical stack of two buttons, .one.right.delta and .one.right.epsilon. These are packed toward the bottom of .one.right, so the first one packed is on the bottom. The frame .one.right was packed to the right, and in the previous example, the button .one.gamma was packed to the left. Despite the difference, they ended up in the same position relative to the other two widgets packed inside the .one frame. The next section explains why.
Top
Table of Contents
# pack two frames on the bottom. frame .one -width 100 -height 50 -bg grey50 frame .two -width 40 -height 40 -bg white pack .one .two -side bottom # pack another frame to the right frame .three -width 20 -height 20 -bg grey75 pack .three -side right When we pack a third frame into the main window with -side left or -side right, the new frame is positioned inside the cavity, which is above the two frames already packed toward the bottom side. The frame does not appear to the right of the existing frames as you might have expected. This is
because the .two frame occupies the whole bottom side of the packing cavity, even though its display does not fill up that side. Can you tell where the packing cavity is after this example? It is to the left of the frame .three, which is the last frame packed toward the right, and it is above the frame .two, which is the last frame packed toward the bottom. This explains why there was no difference between the previous two examples when .one.gamma was packed to the left, but .one.right was packed to the right. At that point, packing to the left or right of the cavity had the same effect. However, it will affect what happens if another widget is packed into those two configurations. Try out the following commands after running Example 23-3 and Example 23-4 and compare the difference.[*]
[*]
Answer: After Example 23-3 the new button is to the right of all buttons. After Example 23-4 the new button is between .one.beta and
.one.right.
button .one.omega -text omega pack .one.omega -side right Each packing parent has its own cavity, which is why introducing nested frames can help. If you use a horizontal or vertical arrangement inside any given frame, you can more easily simulate the packer's behavior in your head!
Top
Table of Contents
frame .one -width 100 -height 50 -bg grey50 frame .two -width 40 -height 40 -bg white # Pack with fill enabled pack .one .two -side bottom -fill x frame .three -width 20 -height 20 -bg red pack .three -side right -fill x
This is just like Example 23-5, except that -fill x has been specified for all the frames. The .two frame fills, but the .three frame does not. This is because the fill does not expand into the packing cavity. In fact, after this example, the packing cavity is the part that shows through in black. Another way to look at this is that the .two frame was allocated the whole bottom side of the packing cavity, so its fill can expand the frame to occupy that space. The .three frame has only been allocated the right side, so a fill in the X direction will not have any effect. Another use of fill is for a menu bar that has buttons at either end and some empty space between them. The frame that holds the buttons is packed toward the top. The buttons are packed into the left and right sides of the menu bar frame. Without fill, the menu bar shrinks to be just large enough to hold all the buttons, and the buttons are squeezed together. When fill is enabled in the X direction, the menu bar fills out the top edge of the display: Example 23-7 Using horizontal fill in a menu bar.
frame .menubar -bg white frame .body -width 150 -height 50 -bg grey50 # Create buttons at either end of the menubar foreach b {alpha beta} { button .menubar.$b -text $b } pack .menubar.alpha -side left pack .menubar.beta -side right # Let the menu bar fill along the top pack .menubar -side top -fill x pack .body
# Create and pack two frames frame .menubar -bg white frame .body -width 150 -height 50 -bg grey50 # Create buttons at either end of the menubar foreach b {alpha beta} { button .menubar.$b -text $b } pack .menubar.alpha -side left -ipady 10 pack .menubar.beta -side right -ipadx 10 # Let the menu bar fill along the top pack .menubar -side top -fill x -ipady 5 pack .body The alpha button is taller and the beta button is wider because of the internal padding. The frame has internal padding, which reduces the space available for the packing cavity, so the .menubar frame shows through above and below the buttons. Some widgets have attributes that result in more display space. For example, it would be hard to distinguish a frame with width 50 and no internal padding from a frame with width 40 and a -ipadx 5 packing option. The packer would give the frame 5 more pixels of display space on either side for a total width of 50. Buttons have their own -padx and -pady options that give them more display space, too. This padding provided by the button is used to keep its text away from the edge of the button. The following example illustrates the difference. The -anchor e button option positions the text as far to the right as possible. Example 37-5 on page 531 provides another comparison of these options: Example 23-9 Button padding vs. packer padding.
# Foo has internal padding from the packer button .foo -text Foo -anchor e -padx 0 -pady 0 pack .foo -side right -ipadx 10 -ipady 10
# Bar has its own padding button .bar -text Bar -anchor e -pady 10 -padx 10 pack .bar -side right -ipadx 0 -ipady 0
. config -borderwidth 10 # OK is the default button frame .ok -borderwidth 2 -relief sunken button .ok.b -text OK pack .ok.b -padx 5 -pady 5 # Cancel is not button .cancel -text Cancel pack .ok .cancel -side left -padx 5 -pady 5 Even if the .ok.b button were packed with -fill both, it would look the same. The external padding provided by the packer will not be filled by the child widgets. Example 23-10 handcrafts the look of a default button. Tk 8.0 has a -default attribute for buttons that gives them the right appearance for the default button on the current platform. It looks somewhat like this on UNIX, but the appearance is different on Macintosh and Windows.
Top
Table of Contents
# Make the main window black . config -bg black # Create and pack two frames frame .menubar -bg white frame .body -width 150 -height 50 -bg grey50
# Create buttons at either end of the menubar foreach b {alpha beta} { button .menubar.$b -text $b } pack .menubar.alpha -side left pack .menubar.beta -side right # Let the menu bar fill along the top pack .menubar -side top -fill x pack .body # Resize the main window to be bigger wm geometry . 200x100 # Allow interactive resizing wm minsize . 100 50 The only widget that claims any of the new space is. menubar because of its -fill x packing option. The .body frame needs to be packed properly: Example 23-12 Resizing with expand turned on.
# Use all of Example 23?1 then repack .body pack .body -expand true -fill both If more than one widget inside the same parent is allowed to expand, then the packer shares the extra space between them proportionally. This is probably not the effect you want in the examples we have built so far. The .menubar, for example, is not a good candidate for expansion. Example 23-13 More than one expanding widget.
# Use all of Example 23?1 then repack .menubar and .body pack .menubar -expand true -fill x pack .body -expand true -fill both
Top
Table of Contents
Anchoring
If a widget is left with more packing space than display space, you can position it within its packing space using the -anchor packing option. The default anchor position is center. The other options correspond to points on a compass: n, ne, e, se, s, sw, w, and nw: Example 23-14 Setup for anchor experiments.
# Make the main window black . config -bg black # Create two frames to hold open the cavity frame .prop -bg white -height 80 -width 20 frame .base -width 120 -height 20 -bg grey50 pack .base -side bottom # Float a label and the prop in the cavity label .foo -text Foo pack .prop .foo -side right -expand true The .base frame is packed on the bottom. Then the .prop frame and the .foo label are packed to the right with expand set but no fill. Instead of being pressed up against the right side, the expand gives each of these widgets half of the extra space in the X direction. Their default anchor of center results in the positions shown. The next example shows some different anchor positions: Example 23-15 The effects of noncenter anchors.
. config -bg black # Create two frames to hold open the cavity frame .prop -bg white -height 80 -width 20 frame .base -width 120 -height 20 -bg grey50 pack .base -side bottom # Float the label and prop # Change their position with anchors label .foo -text Foo pack .prop -side right -expand true -anchor sw pack .foo -side right -expand true -anchor ne The label has room on all sides, so each of the different anchors will position it differently. The .prop frame only has room in the X direction, so it can only be moved into three different positions: left, center, and right. Any of the anchors w, nw, and sw result in the left position. The anchors center, n, and s result in the center position. The anchors e, se , and ne result in the right position. If you want to see all the variations, type in the following commands to animate the different packing anchors. The update idletasks forces any pending display operations. The after 500 causes the script to wait for 500 milliseconds: Example 23-16 Animating the packing anchors. foreach anchor {center n ne e se s sw w nw center} { pack .foo .prop -anchor $anchor # Update the display update idletasks # Wait half a second after 500 }
Top
Table of Contents
Packing Order
The packer maintains an order among the children that are packed into a frame. By default, each new child is appended to the end of the packing order. The most obvious effect of the order is that the children first in the packing order are closest to the side they are packed against. You can control the packing order with the -before and -after packing options, and you can reorganize widgets after they have already been packed: Example 23-17 Controlling the packing order.
# Create five labels in order foreach label {one two three four five} { label .$label -text $label pack .$label -side left -padx 5 } # ShuffleUp moves a widget to the beginning of the order proc ShuffleUp { parent child } { set first [lindex [pack slaves $parent] 0] pack $child -in $parent -before $first } # ShuffleDown moves a widget to the end of the order proc ShuffleDown { parent child } { pack $child -in $parent } ShuffleUp . .five ShuffleDown . .three
Introspection
The pack slaves command returns the list of children in their packing order. The ShuffleUp procedure uses this to find out the first child so that it can insert another child before it. The ShuffleDown procedure is simpler because the default is to append the child to the end of the packing order. When a widget is repacked, then it retains all its packing parameters that have already been set. If you need to examine the current packing parameters for a widget, use the pack info command. pack info .five => -in . -anchor center -expand 0 -fill none -ipadx 0 \ -ipady 0 -padx 0 -pady 0 -side left
Top
Table of Contents
button .a -text hello frame .b pack .a -in .b If you cannot avoid this problem scenario, then you can use the raise command to fix things up. Stacking order is also discussed on page 347. raise .a
Top
Table of Contents
Unpacking a Widget
The pack forget command removes a widget from the packing order. The widget gets unmapped, so it is not visible. If you unpack a parent frame, the packing structure inside it is maintained, but all the widgets inside the frame get unmapped. Unpacking a widget is useful if you want to suppress extra features of your interface. You can create all the parts of the interface, and just delay packing them in until the user requests to see them. Then you can pack and unpack them dynamically.
Top
Table of Contents
Packer Summary
Keep these rules in mind about the packer: Pack vertically (-side top and -side bottom) or horizontally (-side left and -side right) within a frame. Only rarely will a different mixture of packing directions work out the way you want. Add frames to build more complex structures. By default, the packer puts widgets into their parent frame, and the parent frame must be created before the children that are packed into it. If you put widgets into other relatives, remember to create the frames first so the frames stay underneath the widgets packed into them. By default, the packer ignores -width and -height attributes of frames that have widgets packed inside them. It shrinks frames to be just big enough to allow for its border width and to hold the widgets inside them. Use pack propagate to turn off the shrink-wrap behavior. The packer distinguishes between packing space and display space. A widget's display might not take up all the packing space allocated to it. The -fill option causes the display to fill up the packing space in the X or Y directions, or both. The -expand true option causes the packing space to expand into any room in the packing cavity that is otherwise unclaimed. If more than one widget in the same frame wants to expand, then they share the extra space. The -ipadx and -ipady options allocate more display space inside the border, if possible. The -padx and -pady options allocate more packing space outside the border, if possible. This space is never filled by the widget's display.
info command:
This is just like pack configure. Packs one or more widgets according to the options, which are given in Table 23-2. Unpacks the specified windows. Returns the packing parameters of win. Queries or sets the geometry propagation of win, which has other widgets packed inside it. Returns the list of widgets managed by win. Table 23-2. Packing options.
-after win -anchor anchor -before win -expand boolean -fill style -in win -ipadx amount -ipady amount -padx amount -pady amount -side side
Packs after win in the packing order. Anchors: center, n, ne, e, se, s, sw, w, or nw. Packs before win in the packing order. Controls expansion into the unclaimed packing cavity. Controls fill of packing space. Style: x, y, both, or none. Packs inside win. Horizontal internal padding, in screen units. Vertical internal padding, in screen units. Horizontal external padding, in screen units. Vertical external padding, in screen units. Sides: top, right, bottom, or left.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
A Basic Grid
Example 24-1 uses grid to lay out a set of labels and frames in two parallel columns. It takes advantage of the relative placement feature of grid. You do not necessarily have to specify rows and columns. Instead, the order of grid commands and their arguments implies the layout. Each grid command starts a new row, and the order of the widgets in the grid command determines the column. In the example, there are two columns, and iteration of the loop adds a new row. Grid makes each column just wide enough to hold the biggest widget. Widgets that are smaller are centered in their cell. That's why the labels appear centered in their column: Example 24-1 A basic grid.
foreach color {red orange yellow green blue purple} { label .l$color -text $color -bg white frame .f$color -background $color -width 100 -height 2 grid .l$color .f$color }
combination of n, e, w, and s to stick a widget to the top, right, left, and bottom sides of its cell. You can concatenate these letters together (e.g., news) or uses spaces or commas to separate them (e.g., n,e,w,s ). Example 24-2 uses -sticky w to left justify the labels, and -sticky ns to stretch the color frames to the full height of their row: Example 24-2 A grid with sticky settings.
foreach color {red orange yellow green blue purple} { label .l$color -text $color -bg white frame .f$color -background $color -width 100 -height 2 grid .l$color .f$color grid .l$color -sticky w grid .f$color -sticky ns } Example 24-2 uses grid in two ways. The first grid in the loop fixes the positions of the widgets because it is the first time they are assigned to the master. The next grid commands modify the existing parameters; they just adjust the -sticky setting because their row and column positions are already known. You can specify row and column positions explicitly with the -row and -column attribute. This is generally more work than using the relative placement, but it is necessary if you need to dynamically move a widget into a different cell. Example 24-3 keeps track of rows and columns explicitly and achieves the same layout as Example 24-2: Example 24-3 A grid with row and column specifications. set row 0 foreach color {red orange yellow green blue purple} { label .l$color -text $color -bg white frame .f$color -background $color -width 100 grid .l$color -row $row -column 0 -sticky w grid .f$color -row $row -column 1 -sticky ns incr row }
foreach color {red orange yellow green blue purple} { label .l$color -text $color -bg white frame .f$color -background $color -width 100 -height 2 grid .l$color .f$color grid .l$color -sticky w -padx 3 grid .f$color -sticky ns -pady 1 }
foreach color {red orange yellow green blue purple} { label .l$color -text $color -bg white frame .f$color -background $color -width 100 -height 2 grid .l$color .f$color grid .l$color -sticky w -padx 3 -ipady 5 grid .f$color -sticky ns -pady 1 }
Top
Table of Contents
. config -bg white foreach color {888 999 aaa bbb ccc fff} { frame .$color -bg #$color -width 40 -height 40 } grid .888 -row 0 -column 0 -columnspan 3 -sticky news grid .999 -row 1 -column 0 -rowspan 2 -sticky news grid .aaa -row 1 -column 1 -columnspan 2 -sticky news grid .bbb -row 2 -column 2 -rowspan 2 -sticky news grid .ccc -row 3 -column 0 -columnspan 2 -sticky news grid .fff -row 2 -column 1 -sticky news
You can also use special syntax in grid commands that imply row and column placement. Special characters represent a cell that is spanned or skipped:
^ x
A nice feature of the implicit row and column assignments is that it is easy to make minor changes to your layout. Example 24-8 achieves the same layout: Example 24-8 Grid syntax row and column span. . config -bg white foreach color {888 999 aaa bbb ccc ddd fff} { frame .$color -bg #$color -width 40 -height 40 } grid .888 -sticky news grid .999 .aaa -sticky news grid ^ .fff .bbb -sticky news grid .ccc ^ -sticky news
Top
Table of Contents
label .f1 -text left -bg #ccc label .f2 -text right -bg #aaa grid .f1 .f2 -sticky news grid .f1 -padx 10 -pady 10 grid rowconfigure . 0 -pad 20
Minimum Size
The -minsize attribute restricts a column or row to be a minimum size. The row or column can grow bigger if its widget requests it, but they will not get smaller than the minimum. One useful application of -minsize is to create empty rows or columns, which is more efficient than creating an extra frame.
text .text -yscrollcommand ".yscroll set" \ -xscrollcommand ".xscroll set"-width 40 -height 10 scrollbar .yscroll -command ".text yview" -orient vertical scrollbar .xscroll -command ".text xview" -orient horizontal grid .text .yscroll -sticky news
grid .xscroll -sticky ew grid rowconfigure . 0 -weight 1 grid columnconfigure . 0 -weight 1 You can use different weights to let different rows and columns grow at different rates. However, there are some tricky issues because the resize behavior applies to extra space, not total space. For example, suppose there are four columns that have widths 10, 20, 30, and 40 pixels, for a total of 100. If the master frame is grown to 140 pixels wide, then there are 40 extra pixels. If each column has weight 1, then each column gets an equal share of the extra space, or 10 more pixels. Now suppose column 0 has weight 0, columns 1 and 2 have weight 1, and column 3 has weight 2. Column 0 will not grow, columns 1 and 2 will get 10 more pixels, and column 3 will get 20 more pixels. In most cases, weights of 0 or 1 make the most sense. Weight works in reverse when shrinking.
If a row or column has to shrink, the weights are applied in reverse. A row or column with a higher weight will shrink more. For example, put two equal sized frames in columns with different weights. When the user makes the window bigger, the frame in the column with more weight gets larger more quickly. When the window is made smaller, that frame gets smaller more quickly.
Top
Table of Contents
Returns the bounding box, of the whole grid, the cell at c1, r1, or the cells from c1, r1 to c2, r2. Sets or queries the configuration of col. Options are minsize , -weight , and -pad. Grids one or more widgets according to the options, which are given in Table 24-2. Unmaps the specified windows. Returns the grid parameters of win. Returns the cell column and row under the point x, y in master. Enables or disables shrink-wrapping of master. Sets or queries the configuration of row. Options are minsize , -weight , and -pad. Unmaps slave, but remember its configuration. Returns the number of columns and rows. Returns the list of widgets managed by win, or just those in the specified row or column.
grid propagate master ? boolean? grid rowconfigure master row ? options? grid remove slave grid size master grid slaves win ?-row r? ?column c?
Table 24-2 summarizes the grid options for a widget. These are set with the grid configure command, and the current settings are returned by the grid info command.
Places inside win. Column position. Columns count from zero. Spans n columns. Internal widget padding in the X direction, in screen units. Internal widget padding in the Y direction, in screen units. External widget padding in the X direction, in screen units. External widget padding in the Y direction, in screen units. Row position. Rows count from zero. Spans n rows. Positions widget next to any combination of north (n), south (s), east (w), and west (e) sides of the cell. Use {} for center.
Top
Table of Contents
Top
Table of Contents
place
Basics
The place command lets you specify the width and height of a window, and the X and Y locations of the window's anchor point. The size and location can be specified in absolute or relative terms. Relative specifications are more powerful. Example 25-1 uses place to center a window in its parent. You can use this command to position dialogs that you do not want to be detached top-level windows: Example 25-1 Centering a window with place. place $w -in $parent -relx 0.5 -rely 0.5 -anchor center The -relx and -rely specify the relative X and Y positions of the anchor point of the widget $w in $parent . A relative X (or Y) value of zero corresponds to the left (or top) edge of $parent . A value of one corresponds to the right (or bottom) edge of $parent. A value of 0.5 specifies the middle. The anchor point determines what point in $w is positioned according to the specifications. In Example 251 the center anchor point is used so that the center of $w is centered in $parent. The relative height and width settings are used to base a widget's size on another widget. Example 252 completely covers one window with another window. It uses the default anchor point for windows, which is their upper-left hand corner (nw): Example 25-2 Covering a window with place. place $w -in $parent -relwidth 1 -relheight 1 -x 0 -y 0 The absolute and relative size and position parameters are additive (e.g., -width and -relwidth). You can make a window slightly larger or smaller than the parent by specifying both parameters. In Example 25-3, a negative width and height are used to make a window smaller than another one: Example 25-3 Combining relative and absolute sizes.
place $w -in $parent -relwidth 1 -relheight 1 -x 0 -y 0 \ -width -4 -height -4 It is not necessary for $parent to actually be the parent widget of $w. The requirement is that $parent be the parent, or a descendant of the parent, of $w. It also has to be in the same top-level window. This guarantees that $w is visible whenever $parent is visible. These are the same restrictions imposed by the pack geometry manager. It is not necessary to position a widget inside another widget, either. Example 25-4 positions a window five pixels above a sibling widget. If $sibling is repositioned, then $w moves with it. This approach is useful when you decorate a resizable window by placing other widgets at its corners or edges. When the window is resized, the decorations automatically move into place: Example 25-4 Positioning a window above a sibling with place. place $w -in $sibling -relx 0.5 -y -5 -anchor s \ -bordermode outside The -bordermode outside option is specified so that any decorative border in $sibling is ignored when positioning $w. In this case the position is relative to the outside edge of $sibling. By default, the border is taken into account to make it easy to position widgets inside their parent's border. The parent widget does not have to be a frame. Example 25-1 can be used to place a dialog in the middle of a text widget. In Example 25-4, $sibling and $w can both be label widgets.
Top
Table of Contents
proc Pane_Create {f1 f2 args} { # Map optional arguments into array values set t(-orient) vertical set t(-percent) 0.5 set t(-in) [winfo parent $f1] array set t $args # Keep state in an array associated with the master frame set master $t(-in) upvar #0 Pane$master pane array set pane [array get t]
# # # #
Create the grip and set placement attributes that will not change. A thin divider line is achieved by making the two frames one pixel smaller in the adjustable dimension and making the main frame black.
set pane(1) $f1 set pane(2) $f2 set pane(grip) [frame $master.grip -background gray50 \ -width 10 -height 10 -bd 1 -relief raised \ -cursor crosshair] if {[string match vert* $pane(-orient)]} { set pane(D) Y ;# Adjust boundary in Y direction place $pane(1) -in $master -x 0 -rely 0.0 -anchor nw -relwidth 1.0 -height -1 place $pane(2) -in $master -x 0 -rely 1.0 -anchor sw -relwidth 1.0 -height -1 place $pane(grip) -in $master -anchor c -relx 0.8 } else { set pane(D) X ;# Adjust boundary in X direction place $pane(1) -in $master -relx 0.0 -y 0 -anchor nw -relheight 1.0 -width -1 place $pane(2) -in $master -relx 1.0 -y 0 -anchor ne -relheight 1.0 -width -1 place $pane(grip) -in $master -anchor c -rely 0.8 } $master configure -background black # Set up bindings for resize, <Configure>, and # for dragging the grip. bind $master <Configure> [list PaneGeometry $master] bind $pane(grip) <ButtonPress-1> \ [list PaneDrag $master %$pane(D)] bind $pane(grip) <B1-Motion> \ [list PaneDrag $master %$pane(D)] bind $pane(grip) <ButtonRelease-1> \ [list PaneStop $master] # Do the initial layout PaneGeometry $master }
\ \
\ \
All the optional arguments are available in $args. Its attribute-value structure is used to initialize a temporary array t. Default values are set before the assignment from $args. The following code is compact but doesn't check errors in the optional arguments. set t(-orient) vertical set t(-percent) 0.5 set t(-in) [winfo parent $f1] array set t $args Global state about the layout is kept in an array whose name is based on the master frame. The name of the master frame isn't known until after arguments are parsed, which is why t is used. After the upvar the argument values are copied from the temporary array into the global state array: set master $t(-in) upvar #0 Pane$master pane array set pane [array get t]
Event Bindings
The example uses some event bindings that are described in more detail in Chapter 26. The <Configure> event occurs when the containing frame is resized by the user. When the user presses the mouse button over the grip and drags it, there is a <ButtonPress-1> event, one or more <B1-Motion> events, and finally a <ButtonRelease-1> event. Tcl commands are bound to these events: bind $parent <Configure> [list PaneGeometry $parent] bind $pane(grip) <ButtonPress-1> \ [list PaneDrag $parent %$pane(D)] bind $pane(grip) <B1-Motion> \ [list PaneDrag $parent %$pane(D)] bind $pane(grip) <ButtonRelease-1> [list PaneStop $parent]
grip is moved by setting its relative Y position. The size of the two contained frames is set with a relative height. Remember that this is combined with the fixed height of -1 to get some space between the two frames: Example 25-7 PaneGeometry updates the layout. proc PaneGeometry {master} { upvar #0 Pane$master pane if {$pane(D) == "X"} { place $pane(1) -relwidth $pane(-percent) place $pane(2) -relwidth [expr 1.0 - $pane(-percent)] place $pane(grip) -relx $pane(-percent) set pane(size) [winfo width $master] } else { place $pane(1) -relheight $pane(-percent) place $pane(2) -relheight [expr 1.0 - $pane(-percent)] place $pane(grip) -rely $pane(-percent) set pane(size) [winfo height $master] } } proc PaneTest {{p .p} {orient vert}} { catch {destroy $p} frame $p -width 200 -height 200 label $p.1 -bg blue -text foo label $p.2 -bg green -text bar pack $p -expand true -fill both pack propagate $p off Pane_Create $p.1 $p.2 -in $p -orient $orient -percent 0.3 }
Top
Table of Contents
This is just like place configure. Places one or more widgets according to the options, which are given Table 25-2. Unmaps the specified windows. Returns the placement parameters of win. Returns the list of widgets managed by win.
Table 25-2 summarizes the placement options for a widget. These are set with the place configure command, and the current settings are returned by the place info command.
-in win -anchor where -x coord -relx offset -y coord -rely offset -width size -relwidth size -height size -relheight size -bordermode mode
Places inside (or relative to) win. Anchors: center, n, ne, e, se, s, sw, w, or nw. Default: nw. X position, in screen units, of the anchor point. Relative X position. 0.0 is the left edge. 1.0 is the right edge. Y position, in screen units, of the anchor point. Relative Y position. 0.0 is the top edge. 1.0 is the bottom edge. Width of the window, in screen units. Width relative to parent's width. 1.0 is full width. Height of the window, in screen units. Height relative to the parent's height. 1.0 is full height. If mode is inside, then size and position are inside the parent's border. If mode is outside , then size and position are relative to the outer edge of the parent. The default is inside.
Top
Table of Contents
Top
Table of Contents
event. The keywords are substituted with event-specific data before the Tcl command is evaluated. For example, %W is replaced with the widget's pathname. The %X and %Y keywords are replaced with the coordinates of the event relative to the screen. The %x and %y keywords are replaced with the coordinates of the event relative to the widget. The event keywords are summarized on page 379. The % substitutions are performed throughout the entire command bound to an event, without regard to other quoting schemes. You must use %% to obtain a single percent sign. For this reason you should make your binding commands short, adding a new procedure if necessary (e.g., tkMbMotion), instead of littering percent signs throughout your code. A new binding is created by specifying a binding tag, an event sequence, and a command: bind Menubutton <B1-Motion> {tkMbMotion %W down %X %Y} If the first character of the binding command is +, the command (without the +) is added to the commands, if any, for that event and binding tag: bind bindingTag event {+ command args} To delete a binding for an event, bind the event to the null string: bind bindingTag event {} Bindings execute in the global scope.
When a binding is triggered, the command is evaluated at the global scope. A very common mistake is to confuse the scope that is active when the bind command creates a binding, and the scope that is active when the binding is triggered. The same problem crops up with the commands associated with buttons, and it is discussed in more detail at the beginning of Chapter 27.
Top
Table of Contents
particular widget. There are no bindings on this bindtag by default. The widget's class (e.g., Text). The class for a widget is derived from the name of the command that creates it. A button widget has the class Button, a text has the class Text, and so on. The Tk widgets define their default behavior with bindings on their class. The Tk pathname of the widget's top-level window (e.g., .). This is redundant in the case of a top-level widget, so it is not used twice. There are no bindings on this bindtag by default. The bindings on a top-level window can be used in dialog boxes to handle keyboard accelerators. The global binding tag all. The default bindings on all are used to change focus among widgets. They are described on page 517. When there is more than one binding tag on a widget, then one binding from each binding tag can match an event. The bindings are processed in the order of the binding tags. By default, the most specific binding tag comes first, and the most general binding tag comes last. Example 26-1 has two frame widgets that have the following behavior. When the mouse enters them, they turn red. They turn white when the mouse leaves. When the user types <Control-c>, the frame under the mouse is destroyed. One of the frames, .two, reports the coordinates of mouse clicks: Example 26-1 Bindings on different binding tags. frame .one -width 30 -height 30 frame .two -width 30 -height 30 bind Frame <Enter> {%W config -bg red} bind Frame <Leave> {%W config -bg white} bind .two <Button> {puts "Button %b at %x %y"} pack .one .two -side left bind all <Control-c> {destroy %W} bind all <Enter> {focus %W} The Frame class has a binding on <Enter> and <Leave> that changes a frame's background color when the mouse moves in and out of the window. This binding is shared by all the frames. There is also a binding on all for <Enter> that sets the keyboard focus. Both bindings will trigger when the mouse enters a frame.
The break and continue commands control the progression through the set of binding tags. The break command stops the current binding and suppresses the bindings from any remaining tags in the binding set order. The continue command in a binding stops the current binding and continues with the command from the next binding tag. For example, the Entry binding tag has bindings that insert and edit text in a one-line entry widget. You can put a binding on <Return> that executes a Tcl command using the value of the widget. The following example runs Some Command before the \r character is added to the entry widget. The binding is on the name of the widget, which is first in the set of binding tags, so the break suppresses the Entry binding that inserts the character: bind .entry <Return> {Some Command ; break} Note that you cannot use the break or continue commands inside a procedure that is called by the binding. This is because the procedure mechanism will not propagate the break or continue signal. Instead, you could use the -code option to return, which is described on page 80: return -code break
Top
Table of Contents
Event Syntax
The bind command uses the following syntax to describe events: <modifier-modifier-type-detail> <<Event>> The first form is for physical events like keystrokes and mouse motion. The second form is for virtual events like Cut and Paste, which correspond to different physical events on different platforms. Physical events are described in this section. Virtual events are described in more detail on page 378. The primary part of the description is the type, (e.g., Button or Motion). The detail is used in some events to identify keys or buttons, (.e.g., Key-a or Button-1). A modifier is another key or button that is already pressed when the event occurs, (e.g., Control-Key-a or B2-Motion). There can be multiple modifiers (e.g., Control-Shift-x). The < and > delimit a single event. Table 26-1 on the next page lists all physical event types. When two event types are listed together (e.g., ButtonPress and Button) they are equivalent.
Keyboard Events
The KeyPress type is distinguished from KeyRelease so that you can have different bindings for each of these events. KeyPress can be abbreviated Key, and Key can be left off altogether if a detail is given to indicate what key. Finally, as a special case for KeyPress events, the angle brackets can also be left out. The following are all equivalent event specifications: <KeyPress-a> <Key-a> <a> a The detail for a key is also known as the keysym, which refers to the graphic printed on the key of the
keyboard. For punctuation and non-printing characters, special keysyms are defined. Case is significant in keysyms, but unfortunately there is no consistent scheme. In particular BackSpace has a capital B and a capital S. Commonly encountered keysyms include:
Return, Escape, BackSpace, Tab, Up, Down, Left, Right, comma, period, dollar, asciicircum, numbersign, exclam.
The application has been activated. (Macintosh) A button is pressed (down). A button is released (up). The stacking order of the window changed. The color map has changed. The window changed size, position, border, or stacking order. The application has been deactivated. (Macintosh) The window has been destroyed. The mouse has entered the window. The window has been exposed. The window has received focus. The window has lost focus. The window has moved because of a change in size of its parent window. A key is pressed (down). A key is released (up). The scrolling mouse wheel has moved. The mouse is moving in the window. The mouse is leaving the window. The window has been mapped (opened). A property on the window has been changed or deleted. A window has been reparented. The window has been unmapped (iconified). The window has changed visibility.
There are times when you do not know what keysym is generated by a special key on your keyboard. The keysyms are defined by the window system implementation, and on UNIX systems they are affected by a dynamic keyboard map, the X modmap. You may find the next binding useful to determine just what the keysym for a particular key is on your system: bind $w <KeyPress> {puts stdout {%%K=%K %%A=%A}} The %K keyword is replaced with the keysym from the event. The %A is replaced with the printing character that results from the event and any modifiers like Shift. The %% is replaced with a single percent sign. Note that these substitutions occur in spite of the curly braces used for grouping. If the user types a capital Q, there are two KeyPress events, one for the Shift key, and one for the q key. The output is: %K=Shift_R %A={} %K=Q %A=Q The Shift_R keysym indicates the right-hand shift key was pressed. The %A keyword is replaced with {} when modifier keys are pressed. You can check for this in <KeyPress> bindings to avoid doing anything if only a modifier key is pressed. On Macintosh, there is no event at all when the modifier keys are pressed. The following can be used with a text widget. The double quotes are necessary to force a string comparison: bind $w <KeyPress> { if {"%A" != "{}"} {%W insert insert %A} }
Mouse Events
Button events also distinguish between ButtonPress, (or Button), and ButtonRelease. Button can be left off if a detail specifies a button by number. The following are equivalent: <ButtonPress-1> <Button-1> <1> Note: The event <1> implies a ButtonPress event, while the event 1 implies a KeyPress event. To
avoid confusion, always specify the Key or Button type. The mouse is tracked by binding to the Enter, Leave, and Motion events. Enter and Leave are triggered when the mouse comes into and exits out of the widget, respectively. A Motion event is generated when the mouse moves within a widget. The coordinates of the mouse event are represented by the %x and %y keywords in the binding command. The coordinates are widget-relative, with the origin at the upper-left hand corner of a widget's window. The keywords %X and %Y represent the coordinates relative to the screen: bind $w <Enter> {puts stdout "Entered %W at %x %y"} bind $w <Leave> {puts stdout "Left %W at %x %y"} bind $w <Motion> {puts stdout "%W %x %y"} A mouse drag event is a Motion event that occurs when the user holds down a mouse button. In this case the mouse button is a modifier, which is discussed in more detail on page 375. The binding looks like this: bind $w <B1-Motion> {puts stdout "%W %x %y"}
Other Events
The <Map> and <Unmap> events are generated when a window is opened and closed, or when a widget is packed or unpacked by its geometry manager. The <Activate> and <Deactivate> events are generated when an application is activated by the operating system. This applies to Macintosh systems, and it occurs when the user clicks in the application window. The <Configure> event is generated when the window changes size. A canvas that computes its display based on its size can bind a redisplay procedure to the <Configure> event, for example. The <Configure> event can be caused by interactive resizing. It can also be caused by a configure widget command that changes the size of the widget. You should not reconfigure a widget's size while processing a <Configure> event to avoid an indefinite sequence of these events. The <Destroy> event is generated when a widget is destroyed. You can intercept requests to delete windows, too. See also the description of the wm command on page 569. The <MouseWheel> event is generated on Windows by the small scrolling wheel built into the Microsoft Mouse. It reports a delta value using the %D keyword. Currently the delta is an integer multiple of 120, where positive values indicate a scroll up, and negative values indicate a scroll down. Chapter 36 presents some examples that use the <FocusIn> and <FocusOut> events. The remaining events in Table 26-1 have to do with dark corners of the X protocol, and they are seldom used. More information can be found on these events in the Event Reference section of the Xlib Reference Manual (Adrian Nye, O'Reilly & Associates, Inc., 1992).
Be careful when binding events to top-level windows because their name is used as a binding tag on all the widgets contained in them. For example, the following binding fires when the user destroys the main window, which means the application is about to exit: bind . <Destroy> {puts "goodbye"} Unfortunately, all widgets inside the main window are destroyed as a side effect, and they all share the name of their toplevel widget as a binding tag. So this binding fires when every widget inside the main window is destroyed. Typically you only want to do something one time. The following binding checks the identity of the widget before doing anything: bind . <Destroy> {if {"%W" == "."} {puts "goodbye"}}
Top
Table of Contents
Modifiers
A modifier indicates that another key or button is being held down at the time of the event. Typical modifiers are the Shift and Control keys. The mouse buttons can also be used as modifiers. If an event does not specify any modifiers, the presence of a modifier key is ignored by the event dispatcher. However, if there are two possible matching events, the more accurate match will be used. For example, consider these three bindings: bind $w <KeyPress> {puts "key=%A"} bind $w <Key-c> {puts "just a c"} bind $w <Control-Key-c> {exit} The last event is more specific than the others. Its binding will be triggered when the user types c with the Control key held down. If the user types c with the Meta key held down, the second binding will be triggered. The Meta key is ignored because it does not match any binding. If the user types something other than a c, the first binding is triggered. If the user presses the Shift key, then the keysym that is generated is C, not c, so the last two events do not match. There are eight possible modifier keys. The Control, Shift, and Lock modifiers are found on nearly all keyboards. The Meta and Alt modifiers tend to vary from system to system, and they may not be defined at all. They are commonly mapped to be the same as Mod1 or Mod2, and Tk will try to determine how the mappings are set. The Macintosh has a Command modifier that corresponds to the clover-leaf or apple key. The remaining modifiers, Mod3 through Mod5, are sometimes mapped to other special keys. In OpenLook environments, for example, the Paste function key is also mapped to the Mod5 modifier. The button modifiers, B1 through B5, are most commonly used with the Motion event to distinguish different mouse dragging operations. For example, <B1-Motion> is the event generated when the user drags the mouse with the first mouse button held down. Double-click warning.
The Double and Triple events match on repetitions of an event within a short period of time. These are commonly used with mouse events. Be careful: The binding for the regular press event will match on the first press of the Double. Then the command bound to the Double event will match on the second press. Similarly, a Double event will match on the first two presses of a Triple event. Verify this by trying out the following bindings: bind . <1> {puts stdout 1} bind . <Double-1> {puts stdout 2} bind . <Triple-1> {puts stdout 3} If you click the first mouse button several times quickly, you will see a 1, 2, and then a few 3's output. Your bindings must take into consideration that more than one binding might match a Double or Triple event. This effect is compatible with an interface that selects an object with the first click, and then operates on the selected object with a Double event. In an editor, character, word, and line selection on a single, double, and triple click, respectively, is a good example.[*]
[*] If
you really want to disable this, you can experiment with using after to postpone processing of one event. The time constant in the bind implementation of <Double> is 500 milliseconds. At the single-click event, schedule its action to occur after 600 milliseconds, and verify at that time that the <Double> event has not occurred.
The control key. The shift key. The caps-lock key. The command key. (Macintosh) Defined to be what ever modifier (M1 through M5) is mapped to the Meta_L and Meta_R keysyms. Defined to be the modifier mapped to Alt_L and Alt_R. The first modifier. The second modifier. Another modifier. Another modifier. Another modifier. The first mouse button (left).
The second mouse button (middle). The third mouse button (right). The fourth mouse button. The fifth mouse button. Matches double-press event. Matches triple-press event. Matches any combination of modifiers. (Before Tk 4.0)
The UNIX xmodmap program returns the current mappings from keys to these modifiers. The first column of its output lists the modifier. The rest of each line identifies the keysym(s) and low-level keycodes that are mapped to each modifier. The xmodmap program can also be used to change mappings. The following example shows the mappings on my system. Your setup may be different. Example 26-2 Output from the UNIX xmodmap program. xmodmap: up to 3 keys per modifier, (keycodes in parentheses): shift Shift_L (0x6a), Shift_R (0x75) lock Caps_Lock (0x7e) control Control_L (0x53) mod1 Meta_L (0x7f), Meta_R (0x81) mod2 Mode_switch (0x14) mod3 Num_Lock (0x69) mod4 Alt_L (0x1a) mod5 F13 (0x20), F18 (0x50), F20 (0x68)
Top
Table of Contents
Event Sequences
The bind command accepts a sequence of events in a specification, and most commonly this is a sequence of key events. In the following examples, the Key events are abbreviated to just the character detail, and so abc is a sequence of three Key events: bind . a {puts stdout A} bind . abc {puts stdout C} With these bindings in effect, both bindings are executed when the user types abc. The binding for a is executed when a is pressed, even though this event is also part of a longer sequence. This is similar to the behavior with Double and Triple event modifiers. For this reason you must be careful when binding sequences. You can use break in the binding for the prefix to ensure that it does not do anything: bindtags $w [list $w Text [winfo toplevel $w] all] bind $w <Control-x> break bind $w <Control-x><Control-s> {Save ; break} bind $w <Control-x><Control-c> {Quit ; break} The break ensures that the default Text binding that inserts characters does not trigger. This trick is embodied by BindSequence in the next example. If a sequence is detected, then a break binding is added for the prefix. The procedure also supports the emacs convention that <Meta-x> is equivalent to <Escape>x. This convention arose because Meta is not that standard across keyboards. There is no meta key at all on Windows and Macintosh keyboards. The regexp command is used to pick out the detail from the <Meta> event. Example 26-3 Emacs-like binding convention for Meta and Escape. proc BindSequence { w seq cmd } { bind $w $seq $cmd
# Double-bind Meta-key and Escape-key if [regexp {<Meta-(.*)>}$seq match letter] { bind $w <Escape><$letter> $cmd } # Make leading keystroke harmless if [regexp {(<.+>)<.+>}$seq match prefix] { bind $w $prefix break } } The use of break and continue in bindings is not supported in Tk 3.6 and earlier. This is because only a single binding tag can match an event. To make a prefix of a sequence harmless in Tk 3.6, bind a space to it: bind $w $prefix {} This installs a binding for the widget, which suppresses the class binding in Tk 3.6. The space is different than a null string, {}. Binding to a null string deletes the current binding instead of replacing it with a harmless one.
Top
Table of Contents
Virtual Events
A virtual event corresponds to one or more event sequences. When any of the event sequences occurs, then the virtual event occurs. The next example shows the default virtual events for each platform: Example 26-4 Virtual events for cut, copy, and paste. switch $tcl_platform(platform) { "unix" { event add <<Cut>> <Control-Key-x> <Key-F20> event add <<Copy>> <Control-Key-c> <Key-F16> event add <<Paste>> <Control-Key-v> <Key-F18> } "windows" { event add <<Cut>> <Control-Key-x> <Shift-Key-Delete> event add <<Copy>> <Control-Key-c> <Control-Key-Insert> event add <<Paste>> <Control-Key-v> <Shift-Key-Insert> } "macintosh" { event add <<Cut>> <Control-Key-x> <Key-F2> event add <<Copy>> <Control-Key-c> <Key-F3> event add <<Paste>> <Control-Key-v> <Key-F4> } } You can define more than one physical event that maps to the same virtual event: event add <<Cancel>> <Control-c> <Escape> <Command-.> With this definition any of the physical events will trigger a <<Cancel>>. This would be convenient if the same user commonly used your application on different platforms. However, it is also possible that the physical bindings on different platforms overlap in conflicting ways.
By default, virtual event definitions add to existing definitions for the same virtual event. The previous command could be replaced with these three: event add <<Cancel>> <Control-c> event add <<Cancel>> <Escape> event add <<Cancel>> <Command-.> The event command is summarized in Table 26-3.
Adds a mapping from one or more physical events to virtual event virt. Deletes virtual event virt. Returns the defined virtual events. Returns the physical events that map to virt. Generates event for window win. The options are listed in Table 26-4.
Top
Table of Contents
Event Keywords
Table 26-4 lists the percent keywords and the corresponding option to the event generate command. Remember that keyword substitutions occur throughout the command, regardless of other Tcl quoting conventions. Keep your binding commands short, introducing procedures if needed. For the details about various event fields, consult the Xlib Reference Manual (O'Reilly & Associates, Inc.). The string values for the keyword substitutions are listed after a short description of the keyword. If no string values are listed, the keyword has an integer value like a coordinate or a window ID.
Use this to get a single percent sign. All events. The serial number for the event. All events. The above field from the event. Configure event. Button number. Events: ButtonPress and ButtonRelease. The count field. Events: Expose and Map. The detail field. Values: NotifyAncestor, NotifyNonlinearVirtual, NotifyDetailNone, NotifyPointer, NotifyInferior, NotifyPointerRoot , NotifyNonlinear, or NotifyVirtual. Events: Enter, Leave, FocusIn , and FocusOut. The focus field (0 or 1). Events: Enter and Leave. The height field. Events: Configure and Expose. The keycode field. Events: KeyPress and KeyRelease. The mode field. Values: NotifyNormal, NotifyGrab, NotifyUngrab, or NotifyWhileGrabbed. Events: Enter, Leave, FocusIn , and FocusOut. The override_redirect field. Events: Map, Reparent, and Configure.
%f -focus boolean %h -height num %k -keycode num %m -mode value %o -override boolean
The place field. Values: PlaceOnTop, PlaceOnBottom. Circulate event. The state field. A decimal string for events: ButtonPress, ButtonRelease, Enter, Leave, KeyPress, KeyRelease, and Motion. Values for the Visibility event: VisibilityUnobscured, VisibilityPartiallyObscured , or VisibilityFullyObscured.
The time field. All events. The value_mask field. Configure event. The width field. Events: Configure and Expose. The X coordinate, widget relative. Mouse events. The Y coordinate, widget relative. Mouse events. The printing character from the event, or {} . Events: KeyPress and KeyRelease.
%B -borderwidth num %D -delta value %E -sendevent bool %K -keysym symbol %N %R -root win %S -subwindow win %T %W %X -rootx pixel %Y -rooty pixel
The border width. Configure event. The delta value. MouseWheel event. The send_event field. All events. The keysym from the event. Events: KeyPress and KeyRelease. The keysym as a decimal number. Events: KeyPress and KeyRelease. The root window ID. All events. The subwindow ID. All events. The type field. All events. The Tk pathname of the widget receiving the event. All events. The x_root field. Relative to the (virtual) root window. Events: ButtonPress, ButtonRelease, KeyPress, KeyRelease, and Motion. The y_root field. Relative to the (virtual) root window. Events: ButtonPress, ButtonRelease, KeyPress, KeyRelease, and Motion.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
proc Trouble {args} { set b 0 # Display the value of x, a global variable label .label -textvariable x set f [frame .buttons -borderwidth 10] # Create buttons that multiply x by their value foreach val $args { button $f.$b -text $val \ -command "set x \[expr \$x * $val\]" pack $f.$b -side left
incr b } pack .label $f } set x 1 Trouble -1 4 7 36 The example uses a label widget to display the current value of x. The textvariable attribute is used so that the label displays the current value of the variable, which is always a global variable. It is not necessary to have a global command inside Trouble because the value of x is not used there. The button's command is executed later at the global scope. The definition of the button's command is ugly, though. The value of the loop variable val is needed when the button is defined, but the rest of the substitutions need to be deferred until later. The variable substitution of $x and the command substitution of expr are suppressed by quoting with backslashes: set x \[expr \$x * $val\] In contrast, the following command assigns a constant expression to x each time the button is clicked, and it depends on the current value of x, which is not defined the first time through the loop. Clearly, this is incorrect: button $f.$b -text $val \ -command "set x [expr $x * $val]" Another incorrect approach is to quote the whole command with braces. This defers too much, preventing the value of val from being used at the correct time. Use procedures for button commands.
The general technique for dealing with these sorts of scoping problems is to introduce Tcl procedures for use as the button commands. Example 27-2 introduces a little procedure to encapsulate the expression: Example 27-2 Fixing the troublesome situation. proc LessTrouble { args } { set b 0 label .label -textvariable x
set f [frame .buttons -borderwidth 10] foreach val $args { button $f.$b -text $val \ -command "UpdateX $val" pack $f.$b -side left incr b } pack .label $f } proc UpdateX { val } { global x set x [expr $x * $val] } set x 1 LessTrouble -1 4 7 36 It may seem just like extra work to introduce the helper procedure, UpdateX. However, it makes the code clearer in two ways. First, you do not have to struggle with backslashes to get the button command defined correctly. Second, the code is much clearer about the function of the button. Its job is to update the global variable x. You can generalize UpdateX to work on any variable by passing the name of the variable to update. Now it becomes much like the incr command: button $f.$b -text $val -command "Update x $val" The definition of Update uses upvar, which is explained on page 85, to manipulate the named variable in the global scope: proc Update {varname val} { upvar #0 $varname x set x [expr $x * $val] } Double quotes are used in the button command to allow $val to be substituted. Whenever you use quotes like this, you have to be aware of the possible values for the substitutions. If you are not careful, the command you create may not be parsed correctly. The safest way to generate the command is with list: button $f.$b -text $val -command [list UpdateX $val] Using list ensures that the command is a list of two elements, UpdateX and the value of val. This is important because UpdateX takes only a single argument. If val contained white space, then the resulting command would be parsed into more words than you expected. Of course, in this case we plan to always call LessTrouble with an integer value, which does not contain white space.
Example 27-3 provides a more straightforward application of procedures for button commands. In this case the advantage of the procedure MaxLineLength is that it creates a scope for the local variables used during the button action. This ensures that the local variables do not accidentally conflict with global variables used elsewhere in the program. There is also the standard advantage of a procedure, which is that you may find another use for the action in another part of your program. Example 27-3 A button associated with a Tcl procedure.
proc MaxLineLength { file } { set max 0 if [catch {open $file}in] { return $in } foreach line [split [read $in] \n] { set len [string length $line] if {$len > $max} { set max $len } } return "Longest line is $max characters" } # Create an entry to accept the file name, # a label to display the result # and a button to invoke the action . config -borderwidth 10 entry .e -width 30 -bg white -relief sunken button .doit -text "Max Line Length" \ -command {.label config -text [MaxLineLength [.e get]]} label .label -text "Enter file name" pack .e .doit .label -side top -pady 5 The example is centered around the MaxLineLength procedure. This opens a file and loops over the lines finding the longest one. The file open is protected with catch in case the user enters a bogus file name. In that case, the procedure returns the error message from open. Otherwise, the procedure returns a message about the longest line in the file. The local variables in, max, and len are hidden inside the scope of the procedure.
The user interface has three widgets: an entry for user input, the button, and a label to display the result. These are packed into a vertical stack, and the main window is given a border. Obviously, this simple interface can be improved in several ways. There is no Quit button, for example. All the action happens in the button command: .label config -text [MaxLineLength [.e get]] Braces are used when defining the button command so that the command substitutions all happen when the button is clicked. The value of the entry widget is obtained with .e get. This value is passed into MaxLineLength, and the result is configured as the text for the label. This command is still a little complex for a button command. For example, suppose you wanted to invoke the same command when the user pressed <Return> in the entry. You would end up repeating this command in the entry binding. It might be better to introduce a one-line procedure to capture this action so that it is easy to bind the action to more than one user action. Here is how that might look: proc Doit {} { .label config -text [MaxLineLength [.e get]] } button .doit -text "Max Line Length" -command Doit bind .e <Return> Doit Chapter 26 describes the bind command in detail, Chapter 29 describes the label widget, and Chapter 32 describes the entry widget.
Top
Table of Contents
proc ShowChoices { parent varname args } { set f [frame $parent.choices -borderwidth 5] set b 0 foreach item $args { radiobutton $f.$b -variable $varname \ -text $item -value $item pack $f.$b -side left incr b } pack $f -side top } proc ShowBooleans { parent args } { set f [frame $parent.booleans -borderwidth 5] set b 0 foreach item $args {
checkbutton $f.$b -text $item -variable $item pack $f.$b -side left incr b } pack $f -side top } set choice kiwi ShowChoices {}choice apple orange peach kiwi strawberry set Bold 1 ; set Italic 1 ShowBooleans {}Bold Italic Underline The ShowChoices procedure takes as arguments the parent frame, the name of a variable, and a set of possible values for that variable. If the parent frame is null, {}, then the interface is packed into the main window. ShowChoices creates a radiobutton for each value, and it puts the value into the text of the button. It also has to specify the value to assign to the variable when the button is clicked because the default value associated with a radiobutton is the empty string. The ShowBooleans procedure is similar to ShowChoices. It takes a set of variable names as arguments, and it creates a checkbutton for each variable. The default values for the variable associated with a checkbutton are zero and one, which is fine for this example. If you need particular values, you can specify them with the -onvalue and -offvalue options. Radiobuttons and checkbuttons can have commands associated with them, just like ordinary buttons. The command is invoked after the associated Tcl variable has been updated. Remember that the Tcl variable associated with the button is defined in the global scope. For example, you could log the changes to variables as shown in the next example. Example 27-5 A command on a radiobutton or checkbutton. proc PrintByName { varname } { upvar #0 $varname var puts stdout "$varname = $var" } checkbutton $f.$b -text $item -variable $item \ -command [list PrintByName $item] radiobutton $f.$b -variable $varname \ -text $item -value $item \ -command [list PrintByName $varname]
Top
Table of Contents
Button Attributes
Table 27-1 lists the attributes for the button, checkbutton, menubutton, and radiobutton widgets. Unless otherwise indicated, the attributes apply to all of these widget types. Chapters 37, 38, and 39 discuss many of these attributes in more detail. Some attributes are ignored on the Windows and Macintosh platforms because they are not supported by the native button widgets. The table uses the resource name for the attributes, which has capitals at internal word boundaries. In Tcl commands, the attributes are specified with a dash and they are all lowercase. Compare: option add *Menubutton.activeBackground: red .mb configure -activebackground red The first command defines a resource database entry that covers all menubuttons and gives them a red active background. This only affects menubuttons created after the database entry is added. The second command changes an existing menubutton (.mb) to have a red active background. Note the difference in capitalization of background in the two commands. The resource database is introduced on page 311, and Chapter 28 explains how to use the resource database in more detail.
Background color when the mouse is over the button. Text color when the mouse is over the button. Anchor point for positioning the text. The normal background color. A bitmap to display instead of text. Width of the border around the button. Tcl command to invoke when button is clicked.
cursor default direction disabledForeground font foreground height highlightBackground highlightColor highlightThickness image indicatorOn justify menu offValue onValue padX padY relief selectColor selectImage state takeFocus text textVariable underline value variable width wrapLength
Cursor to display when mouse is over the widget. displays as a default button. normal and disabled display as normal button. See page 715 (Tk 8.0).
active up , down, left, right, active. menubutton. (Tk 8.0).
Foreground (text) color when button is disabled. Font for the text. Foreground (text) color. (Also fg). Height, in lines for text, or screen units for images. Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Width of highlight border. Image to display instead of text or bitmap. Boolean that controls if the indicator is displayed: checkbutton, menubutton, or radiobutton. Text justification: center, left, or right. Menu posted when menubutton is clicked. Value for Tcl variable when checkbutton is not selected. Value for Tcl variable when checkbutton is selected. Extra space to the left and right of the button text. Extra space above and below the button text.
flat, sunken, raised, groove, solid
or ridge.
Color for selector. checkbutton or radiobutton. Alternate graphic image for selector: checkbutton or radiobutton. Enabled (normal) or deactivated (disabled). Control focus changes from keyboard traversal. Text to display in the button. Tcl variable that has the value of the text. Index of text character to underline. Value for Tcl variable when radiobutton is selected. Tcl variable associated with the button: checkbutton or radiobutton. Width in characters for text, or screen units for image. Maximum character length before text is wrapped, in screen units.
Top
Table of Contents
Button Operations
Table 27-2 summarizes the operations on button widgets. In the table, $w is a button, checkbutton, radiobutton, or menubutton, except when noted. For the most part, these operations are used by the script libraries that implement the bindings for buttons. The cget and configure operations are the most commonly used by applications.
Returns the value of the specified attribute. Queries or manipulates the configuration information for the widget. Deselects the radiobutton or checkbutton. Set the radiobutton variable to the null string. Set the checkbutton variable to the off value. Redisplays the button several times in alternate colors. Invokes the command associated with the button. Selects the radiobutton or checkbutton, setting the associated variable appropriately.
Top
Table of Contents
menubutton .mb -text Sampler -menu .mb.menu pack .mb -padx 10 -pady 10 set m [menu .mb.menu -tearoff 1] $m add command -label Hello! -command {puts "Hello, World!"} $m add check -label Boolean -variable foo \ -command {puts "foo = $foo"} $m add separator $m add cascade -label Fruit -menu $m.sub1 set m2 [menu $m.sub1 -tearoff 0] $m2 add radio -label apple -variable fruit -value apple $m2 add radio -label orange -variable fruit -value orange $m2 add radio -label kiwi -variable fruit -value kiwi The example creates a menubutton and two menus. The main menu .mb.menu is a child of the menubutton .mb. This relationship is necessary so that the menu displays correctly when the menubutton is selected. Similarly, the cascaded submenu .mb.menu.sub1 is a child of the main menu. The first menu entry is represented by the dashed line. This is a tear-off entry that, when selected, makes a copy of the menu in a new top-level window. This is useful if the menu operations are invoked frequently. The -tearoff 0 argument is used when creating the submenu to eliminate its tear-off entry. The command, radio, and check entries are similar to the corresponding button types. The configuration options for menu entries are similar to those for buttons. The main difference is that the text string in the menu entry is defined with the -label option, not -text. Table 27-6 gives the complete set of options for menu entries. The cascade menu entry is associated with another menu. It is distinguished by the small right arrow in the entry. When you select the entry, the submenu is posted. It is possible to have several levels of cascaded menus. There is no limit to the number of levels, except that your users will complain if you nest too many menus.
A Menu Bar
You can create a menu bar manually by packing several menubuttons into a frame. The default bindings on menubuttons are such that you can drag your mouse over the menu bar and the different
menus will display as you drag over their menubutton. Tk 8.0 lets you create a menu bar as a horizontal menu that is associated with a top-level window. On Windows and UNIX the menu is displayed along the top of the window. On Macintosh this menu replaces the main menu along the top of the screen when the window is activated. The menu bar menu should have all cascade entries so that when you select an entry, another menu is displayed. This is illustrated in Example 27-7. It defines variables that store the names of the menu widgets: set $m [menu .menubar.m$m] This creates a variable named File, Edit, and Help that store the names of the menu widgets. This trick is generalized on page 400 in a package that hides the menu widget names. Example 27-7 A menu bar in Tk 8.0. menu .menubar # attach it to the main window . config -menu .menubar # Create more cascade menus foreach m {File Edit Help} { set $m [menu .menubar.m$m] .menubar add cascade -label $m -menu .menubar.m$m } $File add command -label Quit -command exit # add more menu items...
System Menus
The Tk 8.0 menu bar implementation can add entries to the Windows system menu, the Macintosh Apple menu, and the Help menu on all platforms. This works by recognizing special names. For example, if the menu bar is .menubar, then the special names are .menubar.system, .menubar.apple, and .menubar.help. The Help menu is right justified on all platforms. The Apple menu is normally used by applications for their About... entry. The entries you add to the Apple menu are added to the top of the menu. The System menu appears in the Windows title bar and has entries such as Close and Minimize.
Pop-Up Menus
A pop-up menu is not associated with a menubutton. Instead, it is posted in response to a keystroke or other event in the application. The tk_popup command posts a pop-up menu: tk_popup menu x y ?entry? The last argument specifies the entry to activate when the menu is posted. It is an optional parameter
that defaults to 1, which avoids the tear-off entry in position zero. The menu is posted at the specified X and Y coordinates in its parent widget.
Option Menus
An option menu represents a choice with a set of radio entries, and it displays the current choice in the text of the menubutton. The tk_optionMenu command creates a menubutton and a menu full of radio entries: tk_optionMenu w varname firstValue ?value value ...? The first argument is the pathname of the menubutton to create. The second is the variable name. The third is the initial value for the variable, and the rest are the other choices for the value. The menubutton displays the current choice and a small symbol, the indicator, to indicate it is an option menu.
Top
Table of Contents
Keyboard Traversal
The default bindings for menus allow for keyboard selection of menu entries. The selection process is started by pressing <Alt-x>, where x is the distinguishing letter for a menubutton. The underline attribute of a menubutton is used to highlight the appropriate letter. The underline value is a number that specifies a character position, and the count starts at zero. For example, a File menu with a highlighted F is created like this: menubutton .menubar.file -text File -underline 0 \ -menu .menubar.file.m When the user types <Alt-f> over the main window, the menu is posted. The case of the highlighted letter is not important. After a menu is posted, the arrow keys change the selected entry. The <Up> and <Down> keys move within a menu, and the <Left> and <Right> keys move between adjacent menus. The bindings assume that you create your menus from left to right. If any of the menu entries have a letter highlighted with the -underline option, typing that letter invokes that menu entry. For example, an Export entry that is invoked by typing x can be created like this: .menubar.file.m add command -label Export -underline 1 \ -command File_Export The <space> and <Return> keys invoke the menu entry that is currently selected. The <Escape> key aborts the menu selection and removes the menu.
Top
Table of Contents
A numerical index counting from zero. The activated entry, either because it is under the mouse or has been activated by keyboard traversal. The last menu entry. The same as end. No entry at all. The entry under the given Y coordinate. Use @%y in bindings. A string match pattern to match the label of a menu entry.
Table 27-4 summarizes the complete set of menu operations. In the table, $w is a menu widget.
$w activate index $w add type ?option value? ... $w cget option $w clone
Highlights the specified entry. Adds a new menu entry of the specified type with the given values for various attributes. Returns the value for the configuration option. Makes a linked copy of the menu. This is used to implement tear-offs and menu bars. Returns the configuration information for the menu. Deletes the menu entries from index i1 to i2. Returns the value of option for the specified entry. Queries or modifies the configuration information for the specified menu entry. Returns the numerical value of index. Like add, but inserts the new entry after the specified index. Invokes the command associated with the entry. Displays the menu at the specified coordinates. Displays the cascade menu from entry index. Returns the type of the entry at index. Unmaps the menu. Returns the Y coordinate of the top of the entry.
$w configure ?option? ?value? ... $w delete i1 ?i2? $w entrycget index option $w entryconfigure index ?option? ?value? ... $w index index $w insert type index ?option value? ... $w invoke index $w post x y $w postcascade index $w type index $w unpost $w yposition index
Top
Table of Contents
Menu Attributes
A menu has a few global attributes, and then each menu entry has many button-like attributes that describe its appearance and behavior. Table 27-5 specifies the attributes that apply globally to the menu, unless overridden by a per-entry attribute. The table uses the X resource names, which may have a capital at interior word boundaries. In Tcl commands, use all lowercase and a leading dash.
Background color when the mouse is over a menu entry. Text color when the mouse is over a menu entry. Width of the raised border around active entries. The normal background color for menu entries. Width of the border around all the menu entries. Cursor to display when mouse is over the menu. Foreground (text) color when menu entries are disabled. Default font for the text. Foreground color. (Also fg). Tcl command to run just before the menu is posted. Color for selector in check and radio type entries. Control focus changes from keyboard traversal. True if menu should contain a tear-off entry. Command to execute when menu is torn off. Two arguments are added: the original menu and the new tear-off. (Read-only) normal, menubar, or tearoff. (Tk 8.0).
Table 27-6 describes the attributes for menu entries, as you would use them in a Tcl command (i.e., all lowercase with a leading dash.) The attributes for menu entries are not supported directly by the resource database. However, Example 28-6 on page 411 describes how you can use the resource database for menu entries.
Background color when the mouse is over the entry. Foreground (text) color with mouse is over the entry. Text to display as a reminder about keystroke binding. The normal background color. A bitmap to display instead of text. Puts the entry at the start of a new column. (Tk 8.0). Tcl command to invoke when entry is invoked. Default font for the text. Foreground color. (Also fg). Suppresses the margin reserved for button indicators. (Tk 8.0). Image to display instead of text or bitmap. Text to display in the menu entry. Text justification: center, left, or right. Menu posted when cascade entry is invoked. Variable value when check entry is not selected. Value for Tcl variable when check entry is selected. Color for selector: check and radio entries. The state: normal, active, or disabled Index of text character to underline. Value for Tcl variable when radiobutton entry is selected. Tcl variable associated with the check or radio entry.
Top
Table of Contents
pack $mb -side left menu $menuName -tearoff 1 # Remember the name to menu mapping set menu(menu,$label) $menuName } These procedures are repeated in Example 27-9, except that they use the Tk 8.0 menu bar mechanism. The rest of the procedures in the package are the same with either version of menu bars. Example 27-9 Using the Tk 8.0 menu bar facility. proc Menu_Setup { menubar } { global menu menu $menubar # Associated menu with its main window set top [winfo parent $menubar] $top config -menu $menubar set menu(menubar) $menubar set menu(uid) 0 } proc Menu { label } { global menu if [info exists menu(menu,$label)] { error "Menu $label already defined" } # Create the cascade menu set menuName $menu(menubar).mb$menu(uid) incr menu(uid) menu $menuName -tearoff 1 $menu(menubar) add cascade -label $label -menu $menuName # Remember the name to menu mapping set menu(menu,$label) $menuName } Once the menu is set up, the menu array is used to map from a menu name, like File, to the Tk widget name such as .menubar.mb3. Even though this can be done with a couple of lines of Tcl code, the mapping is put inside the MenuGet procedure to hide the implementation. MenuGet uses return -code error if the menu name is unknown, which changes the error reporting slightly as shown in Example 6-19 on page 80. If the user specifies a bogus menu name, the undefined variable error is caught and a more informative error is raised instead. MenuGet is private to the package, so it does not have an underscore in its name. Example 27-10 MenuGet maps from name to menu. proc MenuGet {menuName} { global menu if [catch {set menu(menu,$menuName)} m] {
return -code error "No such menu: $menuName" } return $m } The procedures Menu_Command, Menu_Check, Menu_Radio, and Menu_Separator are simple wrappers around the basic menu commands. They use MenuGet to map from the menu label to the Tk widget name. Example 27-11 Adding menu entries. proc Menu_Command { menuName label command } { set m [MenuGet $menuName] $m add command -label $label -command $command } proc Menu_Check { menuName label var {command {}}} { set m [MenuGet $menuName] $m add check -label $label -command $command \ -variable $var } proc Menu_Radio {menuName label var {val {}} {command {}}} { set m [MenuGet $menuName] if {[string length $val] == 0} { set val $label } $m add radio -label $label -command $command \ -value $val -variable $var } proc Menu_Separator { menuName } { [MenuGet $menuName] add separator } Creating a cascaded menu also requires saving the mapping between the label in the cascade entry and the Tk pathname for the submenu. This package imposes a restriction that different menus, including submenus, cannot have the same label. Example 27-12 A wrapper for cascade entries. proc Menu_Cascade { menuName label } { global menu set m [MenuGet $menuName] if [info exists menu(menu,$label)] { error "Menu $label already defined"
} set sub $m.sub$menu(uid) incr menu(uid) menu $sub -tearoff 0 $m add cascade -label $label -menu $sub set menu(menu,$label) $sub } Creating the sampler menu with this package looks like this: Example 27-13 Using the menu by name package. Menu_Setup .menubar Menu Sampler Menu_Command Sampler Hello! {puts "Hello, World!"} Menu_Check Sampler Boolean foo {puts "foo = $foo"} Menu_Separator Sampler Menu_Cascade Sampler Fruit Menu_Radio Fruit apple fruit Menu_Radio Fruit orange fruit Menu_Radio Fruit kiwi fruit
Menu Accelerators
The final touch on the menu package is to support accelerators in a consistent way. A menu entry can display another column of information that is assumed to be a keystroke identifier to remind users of a binding that also invokes the menu entry. However, there is no guarantee that this string is correct, or that if the user changes the binding that the menu will be updated. Example 27-14 shows the Menu_Bind procedure that takes care of this. Example 27-14 Keeping the accelerator display up to date. proc Menu_Bind { what sequence menuName label } { global menu set m [MenuGet $menuName] if [catch {$m index $label}index] { error "$label not in menu $menuName" } set command [$m entrycget $index -command] bind $what $sequence $command $m entryconfigure $index -accelerator $sequence } The Menu_Bind command uses the index operation to find out what menu entry has the given label. It
gets the command for that entry with entrycget and uses this command in a binding. It updates the display of the accelerator using the entryconfigure operation. This approach has the advantage of keeping the keystroke command consistent with the menu command, as well as updating the display. To try Menu_Bind, add an empty frame to the sampler example, and bind a keystroke to it and one of the menu commands, like this: frame .body -width 100 -height 50 pack .body ; focus .body Menu_Bind .body <space> Sampler Hello!
Top
Table of Contents
Top
Table of Contents
An Introduction to Resources
When a Tk widget is created, its attributes are set by one of three sources. It is important to note that Tcl command specifications have priority over resource database specifications: The most evident source of attributes are the options in Tcl commands, such as the -text quit attribute specification for a button. If an attribute is not specified on the command line, then the resource database is queried as described later. If there is nothing in the resource database, then a hard-coded value from the widget implementation is used. The resource database consists of a set of keys and values. Unlike many databases, however, the keys are patterns that are matched against the names of widgets and attributes. This makes it possible to specify attribute values for a large number of widgets with just a few database entries. In addition, the resource database can be shared by many applications, so users and administrators can define common attributes for their whole set of applications. The resource database is maintained in main memory by the Tk toolkit. On UNIX the database is initialized from the RESOURCE_MANAGER property on the root window, or the .Xdefaults file in your home directory. On Windows and Macintosh there are a few resources added by the tk.tcl library file. Additional files can be explicitly loaded with the option readfile command, and individual database entries are added with the option add Tcl command. The initialization of the database is different from the Xt toolkit, which loads specifications from as many as five different files to allow per-user, per-site, per-application, per-machine, and per-user-perapplication specifications. You can achieve the same effect in Tk, but you must do it yourself. Example 42-1 on page 584 gives a partial solution.
Resource Patterns
The pattern language for the keys is related to the naming convention for Tk widgets. Recall that a widget name reflects its position in the hierarchy of windows. You can think of the resource names as
extending the hierarchy one more level at the bottom to account for all the attributes of each individual widget. There is also a new level of the hierarchy at the top to specify the application by name. For example, the database could contain an entry like the following in order to define a font for the quit button in a frame called .buttons: Tk.buttons.quit.font: fixed The leading Tk. matches the default class name for Tcl/Tk applications. You could also specify a more specific application name, such as exmh, or an asterisk to match any application: *buttons.quit.font: fixed Resource keys can also specify classes of widgets and attributes as opposed to individual instances. The quit button, for example, is an instance of the Button class. Class names for widgets are the same as the Tcl command used to create them, except for a leading capital. A class-oriented specification that would set the font for all buttons in the .buttons frame would be: Tk.buttons.Button.font: fixed Patterns let you replace one or more components of the resource name with an asterisk (*). For example, to set the font for all the widgets packed into the .buttons frame, you could use the resource name *buttons*font. Or, you could specify the font for all buttons with the pattern *Button.font. In these examples we have replaced the leading Tk. with an asterisk as well. It is the ability to collapse several layers of the hierarchical name with a single asterisk that makes it easy to specify attributes for many widgets with just a few database entries. The tables in this book list attributes by their resource name. The resource names use a capital letter at the internal word boundaries. For example, if the command line switch is -offvalue, then the corresponding resource name is offValue. There are also class names for attributes, which are distinguished with a leading capital (e.g., OffValue). Warning: Order is Important!
The matching between a widget name and the patterns in the database can be ambiguous. It is possible that multiple patterns can match the same widget. The way this is resolved in Tk is by the ordering of database entries, with later entries taking precedence. (This is different from the Xt toolkit, in which longer matching patterns have precedence, and instance specifications have priority over class specifications.) Suppose the database contained just two entries, in this order:
*Text*foreground: blue *foreground: red Despite the more specific *Text*foreground entry, all widgets will have a red foreground, even text widgets. For this reason you should list your most general patterns early in your resource files and give the more specific patterns later. Tk also supports different priorities among resources as described in the next section. The ordering precedence described here applies to all resources with the same priority.
Top
Table of Contents
Example 28-2 A file containing resource specifications. ! ! Grey color set ! These values match those used by the Tk widgets on UNIX ! *background: #d9d9d9 *foreground: black *activeBackground: #ececec *activeForeground: black *selectColor: #b03060 *selectBackground: #c3c3c3 *troughColor: #c3c3c3 *disabledforeground:#a3a3a3 The example resource file specifies the color scheme for the Tk widget set on UNIX that is based on a family of gray levels. Color highlighting shows up well against this backdrop. These colors are applied generically to all the widgets. The hexadecimal values for the colors specify two digits (eight bits) each for red, green, and blue. Chapter 38 describes the use of color in detail.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
User-Defined Buttons
Suppose you want users to be able to define a set of their own buttons for frequently executed commands. Or, perhaps users can augment the application with their own Tcl code. The following scheme, which is based on an idea from John LoVerso, lets them define buttons to invoke their own code or their favorite commands. The application creates a special frame to hold the user-defined buttons and places it appropriately. Assume the frame is created like this: frame .user -class User The class specification for the frame means that we can name resources for the widgets inside the frame relative to *User. Users specify the buttons that go in the frame via a personal file containing resource specifications. The first problem is that there is no means to enumerate the database, so we must create a resource that lists the names of the user-defined buttons. We use the name buttonlist and make an entry for *User.buttonlist that specifies which buttons are being defined. It is possible to use artificial resource names (e.g., buttonlist), but they must be relative to an existing Tk widget. Example 28-3 Using resources to specify user-defined buttons. *User.buttonlist: save search justify quit *User.save.text: Save *User.save.command: File_Save *User.search.text: Search *User.search.command: Edit_Search *User.justify.text: Justify *User.justify.command: Edit_Justify *user.quit.text: Quit *User.quit.command: File_Quit *User.quit.background: red
In this example, we have listed four buttons and specified some of the attributes for each, most importantly the text and command attributes. We are assuming, of course, that the application manual publishes a set of commands that users can invoke safely. In this simple example the commands are all one word, but there is no problem with multiword commands. There is no interpretation done of the value, so it can include references to Tcl variables and nested command calls. The following code uses these resource specifications to define the buttons. Example 28-4 Resource_ButtonFrame defines buttons based on resources. proc Resource_ButtonFrame { f class } { frame $f -class $class -borderwidth 2 pack $f -side top -fill x foreach b [option get $f buttonlist {}] { if [catch {button $f.$b}] { button $f.$b -font fixed } pack $f.$b -side right } } The catch phrase is introduced to handle a common problem with fonts and widget creation. If the user's resources specify a bogus or missing font, then the widget creation command will fail. The catch phrase guards against this case by falling back to the fixed font, which is guaranteed to exist. This problem is fixed in Tk 8.0 because the font mechanism will search for alternate fonts. Example 28-5 assumes the resource specifications from Example 28-2 are in the file button.resources. It creates the user-defined buttons in the .users frame. Example 28-5 Using Resource_ButtonFrame.
Top
Table of Contents
User-Defined Menus
User-defined menus can be set up with a similar scheme. However, it is more complex because there are no resources for specific menu entries. We must use more artificial resources to emulate this. We use menulist to name the set of menus. Then, for each of these, we define an entrylist resource. Finally, for each entry we define a few more resources. The name of the entry has to be combined with some type information, which leads to the following convention:
l_entry
t_entry is the type of the entry. c_entry is the command associated with the entry. v_entry m_entry
is the variable associated with the entry. is the menu associated with the entry.
*User.stuff.m.l_keep: Keep on send *User.stuff.m.t_keep: check *User.stuff.m.v_keep: checkvar *User.stuff.m.l_insert: Insert File... *User.stuff.m.c_insert: InsertFileDialog *User.stuff.m.l_find: Find *User.stuff.m.t_find: cascade *User.stuff.m.m_find: find *User.stuff.m.find.entrylist: next prev *User.stuff.m.find.tearoff: 0 *User.stuff.m.find.l_next: Next *User.stuff.m.find.c_next: Find_Next *User.stuff.m.find.l_prev: Previous *User.stuff.m.find.c_prev: Find_Previous In the example, .user.stuff is a Tk menubutton. It has a menu as its child, .user.stuff.m, where the menu .m is set by convention. You will see this later in the code for Resource_Menubar. The entrylist for the menu is similar in spirit to the buttonlist resource. For each entry, however, we have to be a little creative with the next level of resource names. The following does not work: *User.stuff.m.keep.label: Keep on send The problem is that Tk does not directly support resources for menu entries, so it assumes .stuff.m.keep is a widget pathname, but it is not. You can add the resource, but you cannot retrieve it with option get. Instead, we must combine the attribute information (i.e., label) with the name of the entry: *User.stuff.m.l_keep: Keep on send You must do something similar if you want to define resources for items on a canvas, too, because that is not supported directly by Tk. The code to support menu definition by resources is shown in the next example: Example 28-7 Defining menus from resource specifications. proc Resource_Menubar { f class } { set f [frame $f -class $class] pack $f -side top foreach b [option get $f menulist {}] { set cmd [list menubutton $f.$b -menu $f.$b.m \ -relief raised] if [catch $cmd t] { eval $cmd {-font fixed} } if [catch {menu $f.$b.m}] {
menu $f.$b.m -font fixed } pack $f.$b -side left ResourceMenu $f.$b.m } } proc ResourceMenu { menu } { foreach e [option get $menu entrylist {}] { set l [option get $menu l_$e {}] set c [option get $menu c_$e {}] set v [option get $menu v_$e {}] switch -- [option get $menu t_$e {}] { check { $menu add checkbutton -label $l -command $c \ -variable $v } radio { $menu add radiobutton -label $l -command $c \ -variable $v -value $l } separator { $menu add separator } cascade { set sub [option get $menu m_$e {}] if {[string length $sub] != 0} { set submenu [menu $menu.$sub] $menu add cascade -label $l -command $c \ -menu $submenu ResourceMenu $submenu } } default { $menu add command -label $l -command $c } } } }
l-buttonlist
lbuttonlist
- the site-specific list of buttons to add - the per-user list of buttons to remove
u-buttonlist ubuttonlist
This idea and the initial implementation was contributed to exmh by Achim Bonet. The Resource_GetFamily procedure merges five sets of resources shown above. It can replace the option get commands for the buttonlist, menulist, and entrylist resources in Examples 28-4 and 28-7: Example 28-8 Resource_GetFamily merges user and application resources. proc Resource_GetFamily { w resname } { set res [option get $w $resname {}] set lres [option get $w l$resname {}] set ures [option get $w u$resname {}] set l-res [option get $w l-$resname {}] set u-res [option get $w u-$resname {}] # Site-local deletions from application resources set list [lsubtract $res ${l-res}] # Site-local additions set list [concat $list $lres] # Per-user deletions set list [lsubtract $list ${u-res}] # Per-user additions return [concat $list $ures] } proc lsubtract { orig nuke } { # Remove elements in $nuke from $orig foreach x $nuke { set ix [lsearch $orig $x] if {$ix >= 0} { set orig [lreplace $orig $ix $ix] } } return $orig }
Expanding Variables
If the command resource contains substitution syntax like $ and [], then these are evaluated later when the command is invoked by the button or menu. This is because there is no interpretation of the command value when the widgets are created. However, it may be that you want variables substituted when the buttons and menus are defined. You can use the subst command to do this: set cmd [$button cget -command] $button config -command [subst $cmd]
Choosing the scope for the subst can be tricky. The previous command does the subst in the current scope. If this is the Resource_ButtonFrame procedure, then there are no interesting applicationspecific variables defined. The next command uses uplevel to do the subst in the scope of the caller of Resource_ButtonFrame. The list is necessary so that uplevel preserves the structure of the original subst command. $button config -command [uplevel [list subst $cmd]] If you do a subst in ResourceMenu, then you need to keep track of the recursion level to get back to the scope of the caller of Resource_Menubar. The next few lines show what changes in ResourceMenu : proc ResourceMenu {menu {level 1}} { foreach e [option get $menu entrylist {}] { # code omitted set c [option get $menu c_$e {}] set c [uplevel $level [list subst $c]] # And the recursive call is ResourceMenu $submenu [expr $level+1] # more code omitted } } If you want the subst to occur in the global scope, use this: $button config -command [uplevel #0 [list subst $cmd]] However, the global scope may not be much different when you define the button than when the button is invoked. In practice, I have used subst to capture variables defined in the procedure that calls Resource_Menubar.
Top
Table of Contents
Top
Table of Contents
background borderWidth class colormap container cursor height highlightBackground highlightColor highlightThickness menu relief screen takeFocus use visual width
Background color (also bg). Extra space around the edge of the frame. Resource class and binding class name. The value is new or the name of a window. If true, frame embeds another application. Cursor to display when mouse is over the frame. Height, in screen units. Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. The menu to use for the menubar. Toplevel only.
flat, sunken, raised, groove, solid
or ridge.
An X display specification. (Toplevel only, and this cannot be specified in the resource database). Controls focus changes from keyboard traversal. A window ID from winfo id. This embeds the frame or toplevel into the specified window. Type: staticgrey, greyscale, staticcolor, pseudocolor,
directcolor, or truecolor.
You cannot change the class, colormap, visual, or screen attributes after the frame or toplevel has been created. These settings are so fundamental that you need to destroy the frame and start over if you must change them.
On Windows and Macintosh there are several styles of toplevel windows. They differ in their appearance and their behavior. On UNIX, toplevel windows are usually decorated by the window manager, which is a separate application. Chapter 41 describes how to interact with the window manager. On Macintosh, Tk has an unsupported1 command that you can use to set the window style: unsupported1 style window style The possible values for style include documentProc, dBoxProc, plainDBox, altDBoxProc, movableDBoxProc, zoomDocProc, rDocProc, floatProc, floatZoomProc, floatSideProc, or floatSideZoomProc . The dBoxProc, plainDBox, and altDBoxProc styles have no title bar, so there is no close box on them. The other styles have different title bars, a close box, and possibly a full-sized zoom box. The default style is documentProc. I used the following code to see what each looked like: Example 29-1 Macintosh window styles. set x {documentProc dBoxProc plainDBox altDBoxProc \ movableDBoxProc zoomDocProc rDocProc floatProc \ floatZoomProc floatSideProc floatSideZoomProc} foreach y $x { toplevel .$y label .$y.l -text $y pack .$y.l -padx 40 -pady 20 if [catch {unsupported1 style .$y $y}err] { puts "$y: $err" } } This feature may appear as part of the wm command in future releases of Tk. On Windows you can get a couple different styles by using transient and overrideredirect windows, which are described on page 576.
Top
Table of Contents
set maxWidth [string length $value] } } # Use -anchor w to left-justify short strings label $name -width $maxWidth -anchor w \ -text [lindex $values 0] return $name } The FixedWidthLabel example is used to create a label with a width big enough to hold a set of different strings. It uses the -anchor w attribute to left-justify strings that are shorter than the maximum. You can change the text for the label later by using the configure widget operation, which can be abbreviated to config: FixedWidthLabel .status {OK Busy Error} .status config -text Busy A label can display a bitmap or image instead of a text string, which is described in Chapter 38 and the section on Bitmaps and Images. This example could use the font metrics facilities of Tk 8.0 to get more accurate sizes of the text for different strings. It is possible, for example, that a three-character string like OOO is wider than a four-character string like llll in a variable-width font. The font metrics command is described on page 554.
Label Attributes
Table 29-2 lists the widget attributes for the label widget. The attributes are named according to their resource name, which includes a capital letter at internal word boundaries. When you specify an attribute as an option in a Tcl command when creating or reconfiguring a widget, however, you specify the attribute with a dash and all lowercase letters. Chapter 28 explains how to use resource specifications for attributes. Chapters 37, 38, and 39 discuss many of these attributes in more detail.
Relative position of the label within its packing space. Background color (also bg). Name of a bitmap to display instead of a text string. Extra space around the edge of the label. Cursor to display when mouse is over the label. Font for the label's text. Foreground color (also fg). In screen units for bitmaps, in lines for text. Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. Specifies image to display instead of bitmap or text. Text justification: left, right, or center. Extra space to the left and right of the label. Extra space above and below the label.
flat, sunken, raised, groove, solid
or ridge.
Controls focus changes from keyboard traversal. Text to display. Name of Tcl variable. Its value is displayed. Index of character to underline. Width. In characters for text labels. Length at which text is wrapped in screen units.
Top
Table of Contents
message .msg -justify center -text "This is a very long text\ line that will be broken into many lines by the\ message widget" pack .msg A newline in the string forces a line break in the message display. You can retain exact control over the formatting by putting newlines into your string and specifying a very large aspect ratio. In Example 29-4, grouping with double quotes is used to continue the string over more than one line. The newline character between the quotes is included in the string, and it causes a line break:
message .msg -aspect 1000 -justify left -text \ "This is the first long line of text, and this is the second line." pack .msg One disadvantage of a message widget is that, by default, you cannot select the text it displays. Chapter 35 describes how to define custom selection handlers, so you could define one that returned the message string. The message widget predates the text widget, which has many more features and can emulate the message widget. If selections, multiple fonts, and other formatting are important, use a text widget instead of a message widget. Text widgets are described in Chapter 33.
Message Attributes
Table 29-3 lists the attributes for the message widget. The table lists the resource name, which has capitals at internal word boundaries. In Tcl commands these options are specified with a dash and all lowercase:
Relative position of the text within its packing space. 100 * width / height. Default 150. Background color (also bg). Extra space around the edge of the text. Cursor to display when mouse is over the widget. Font for the message's text. Foreground color (also fg). Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. Justification: left, center, or right.
Extra space to the left and right of the text. Extra space above and below the text.
flat, sunken, raised, groove, solid
or ridge.
Controls focus changes from keyboard traversal. Text to display. Name of Tcl variable. Its value is displayed. Width, in screen units.
Top
Table of Contents
scale .scale -from -10 -to 20 -length 200 -variable x \ -orient horizontal -label "The value of X" \ -tickinterval 5 -showvalue true pack .scale Example 29-5 shows a scale for a variable that ranges in value from -10 to +20. The variable x is defined at the global scope. The tickinterval option results in the labels across the bottom, and the
(i.e., pixels).
Scale Bindings
Table 29-4 lists the bindings for scale widgets. You must direct focus to a scale explicitly for the key bindings like <Up> and <Down> to take effect.
Clicking on the trough moves the slider by one unit of resolution toward the mouse click. Clicking on the trough moves the slider all the way to the end of the trough toward the mouse click. Moves the slider toward the left (top) by one unit. Moves the slider toward the left (top) by the value of the bigIncrement attribute. Moves the slider toward the right (bottom) one unit. Moves the slider toward the right (bottom) by the value of the bigIncrement attribute. Moves the slider all the way to the left (top). Moves the slider all the way to the right (bottom).
Scale Attributes
Table 29-5 lists the scale widget attributes. The table uses the resource name, which has capitals at internal word boundaries. In Tcl commands the attributes are specified with a dash and all lowercase.
Background color when the mouse is over the slider. The background color (also bg in commands). Coarse grain slider adjustment value. Extra space around the edge of the widget. Command to invoke when the value changes. The current value is appended as another argument
cursor digits from font foreground highlightBackground highlightColor highlightThickness label length orient relief repeatDelay repeatInterval resolution showValue sliderLength sliderRelief state takeFocus tickInterval to troughColor variable
Cursor to display when mouse is over the widget. Number of significant digits in scale value. Minimum value. The left or top end of the scale. Font for the label. Foreground color (also fg). Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. A string to display with the scale. The length, in screen units, of the long axis of the scale.
horizontal
or vertical. or ridge.
Delay before keyboard auto-repeat starts. Auto-repeat is used when pressing <Button-1> on the trough. Time period between auto-repeat events. The value is rounded to a multiple of this value. If true, value is displayed next to the slider. The length, in screen units, of the slider. The relief of the slider.
normal, active,
or disabled.
Controls focus changes from keyboard traversal. Spacing between tick marks. Zero means no marks. Maximum value. Right or bottom end of the scale. The color of the bar on which the slider sits. Name of Tcl variable. Changes to the scale widget are reflected in the Tcl variable value, and changes in the Tcl variable are reflected in the scale display. Width of the trough, or slider bar.
width
Programming Scales
The scale operations are primarily used by the default bindings and you do not need to program the scale directly. Table 29-6 lists the operations supported by the scale. In the table, $w is a scale widget.
Returns the value of the configuration option. Queries or modifies the widget configuration. Returns the coordinates of the point in the trough that corresponds to value, or the scale's value. Returns the value of the scale, or the value that corresponds to the position given by x and y. Returns trough1, slider, or trough2 to indicate what is under the position given by x and y. Sets the value of the scale.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Using Scrollbars
The following commands create a text widget and two scrollbars that scroll it horizontally and vertically: scrollbar .yscroll -command {.text yview} -orient vertical scrollbar .xscroll -command {.text xview} -orient horizontal text .text -yscrollcommand {.yscroll set} \ -xscrollcommand {.xscroll set} The scrollbar's set operation is designed to be called from other widgets when their display changes. The scrollable widget's xview and yview operations are designed to be called by the scrollbar when the user manipulates them. Additional parameters are passed to these operations as described later. In most cases you can ignore the details of the protocol and just set up the connection between the scrollbar and the widget. Example 30-1 A text widget and two scrollbars.
-xscrollcommand [list $f.xscroll set] \ -yscrollcommand [list $f.yscroll set]}$args scrollbar $f.xscroll -orient horizontal \ -command [list $f.text xview] scrollbar $f.yscroll -orient vertical \ -command [list $f.text yview] grid $f.text $f.yscroll -sticky news grid $f.xscroll -sticky news grid rowconfigure $f 0 -weight 1 grid columnconfigure $f 0 -weight 1 return $f.text } set t [Scrolled_Text .f -width 40 -height 8] pack .f -side top -fill both -expand true set in [open /etc/passwd] $t insert end [read $in] close $in Example 30-1 defines Scrolled_Text that creates a text widget with two scrollbars. It reads and inserts the password file into the text widget. There is not enough room to display all the text, and the scrollbars indicate how much text is visible. Chapter 33 describes the text widget in more detail. The list command constructs the -command and -xscrollcommand values. Even though one could use double quotes here, you should make a habit of using list when constructing values that are used later as Tcl commands. Example 30-1 uses args to pass through extra options to the text widget. The use of eval and args is explained in Example 10-3 on page 127. The scrollbars and the text widget are lined up with the grid geometry manager as explained in Example 24-10 on page 355.
Top
Table of Contents
proc Scroll_Set {scrollbar geoCmd offset size} { if {$offset != 0.0 || $size != 1.0} { eval $geoCmd ;# Make sure it is visible } $scrollbar set $offset $size } takes a geometry management command as an argument, which it uses to make the scrollbar visible. Example 30-3 uses Scroll_Set with a listbox. Note that it does not grid the scrollbars directly. Instead, it lets Scroll_Set do the geometry command the first time it is necessary.
Scroll_Set
proc Scrolled_Listbox { f args } { frame $f listbox $f.list \ -xscrollcommand [list Scroll_Set $f.xscroll \ [list grid $f.xscroll -row 1 -column 0 -sticky we]] \ -yscrollcommand [list Scroll_Set $f.yscroll \ [list grid $f.yscroll -row 0 -column 1 -sticky ns]] eval {$f.list configure}$args scrollbar $f.xscroll -orient horizontal \ -command [list $f.list xview] scrollbar $f.yscroll -orient vertical \ -command [list $f.list yview] grid $f.list -sticky news grid rowconfigure $f 0 -weight 1 grid columnconfigure $f 0 -weight 1 return $f.list } takes optional parameters for the listbox. It uses eval to configure the listbox with these arguments. The style of using eval shown here is explained in Example 10-3 on page 127. Example 43-4 on page 596 associates two listboxes with one scrollbar.
Scrolled_Listbox
The xview and yview operations are designed to be called from scrollbars, and they work the same for all scrollable widgets. You can use them to scroll the widgets for any reason, not just when the scrollbar is used. The following examples use a text widget named .text for illustration. The xview and yview operations return the current first and last values that would be passed to a scrollbar set command: .text yview => 0.2 0.55 When the user clicks on the arrows at either end of the scrollbar, the scrollbar adds scroll num units to its command, where num is positive to scroll down, and negative to scroll up. Scrolling up one line is indicated with this command: .text yview scroll -1 units When the user clicks above or below the elevator of the scrollbar, the scrollbar adds scroll num pages to its command. Scrolling down one page is indicated with this command: .text yview scroll 1 pages You can position a widget so that the top (or left) edge is at a particular offset from the beginning of the widget's contents. The offset is expressed as a floating point value between zero and one. To view the beginning of the contents: .text yview moveto 0.0 If the offset is 1.0, the last part of the widget content's is displayed. The Tk widgets always keep the end of the widget contents at the bottom (or right) edge of the widget, unless the widget is larger than necessary to display all the contents. You can exploit this with the one-line entry widget to view the end of long strings: .entry xview moveto 1.0
to the other widget (e.g., an xview or yview operation), it passes a single extra parameter that specifies what unit to display at the top (left) of the associated widget. The Tk widgets' xview and yview operations are also backward compatible with this interface.
Top
Table of Contents
Scrollbar Bindings
Table 30-1 lists the default bindings for scrollbars on UNIX. Button 1 and button 2 of the mouse have the same bindings. You must direct focus to a scrollbar explicitly for the key bindings like <Up> and <Down> to take effect.
<Button-1> <Button-2> <B1-Motion> <B2-Motion> <Control-Button-1> <ControlButton-2> <Up> <Down> <Control-Up> <Control-Down> <Left> <Right> <Control-Left> <ControlRight> <Prior> <Next> <Home> <End>
Clicking on the arrows scrolls by one unit. Clicking on the trough moves by one screenful. Dragging the slider scrolls dynamically. Clicking on the trough or arrow scrolls all the way to the beginning (end) of the widget. Scrolls up (down) by one unit. Scrolls up (down) by one screenful. Scrolls left (right) by one unit. Scrolls left (right) by one screenful. Scrolls back (forward) by one screenful. Scrolls all the way to the left (top). Scrolls all the way to the right (bottom).
Scrollbar Attributes
Table 30-2 lists the scrollbar attributes. The table uses the resource name for the attribute, which has capitals at internal word boundaries. In Tcl commands, the attributes are specified with a dash and all lowercase. There is no length attribute for a scrollbar. Instead, a scrollbar is designed to be packed next to another widget with a fill option that lets the scrollbar display grow to the right size. Only the relief of the active element can be set. The background color is used for the slider, the arrows, and the border. The slider and arrows are displayed in the activeBackground color when the mouse is over them. The trough is always displayed in the troughColor.
activeBackground activeRelief background borderWidth command cursor elementBorderWidth highlightBackground highlightColor highlightThickness elementBorderWidth jump orient repeatDelay repeatInterval troughColor width
Color when the mouse is over the slider or arrows. Relief of slider and arrows when mouse is over them. The background color (also bg in commands). Extra space around the edge of the scrollbar. Prefix of the command to invoke when the scrollbar changes. Typically this is a xview or yview operation. Cursor to display when mouse is over the widget. Border width of arrow and slider elements. Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. Width of 3D border on arrows and slider. If true, dragging the elevator does not scroll dynamically. Instead, the display jumps to the new position. Orientation: horizontal or vertical. Milliseconds before auto-repeat starts. Auto-repeat is used when pressing <Button-1> on the trough or arrows. Milliseconds between auto-repeat events. The color of the bar on which the slider sits. Width of the narrow dimension of the scrollbar.
Programming Scrollbars
The scrollbar operations are primarily used by the default bindings. Table 30-3 lists the operations supported by the scrollbar. In the table, $w is a scrollbar widget.
$w activate ? element? $w cget option $w configure ... $w delta dx dy $w fraction x y $s get $w identify x y $w set first last
Queries or sets the active element, which can be arrow1, arrow2, or slider. Returns the value of the configuration option. Queries or modifies the widget configuration. Returns the change in the first argument to set required to move the scrollbar slider by dx or dy. Returns a number between 0 and 1 that indicates the relative location of the point in the trough. Returns first and last from the set operation. Returns arrow1, trough1, slider, trough2, or arrow2, to indicate what is under the point. Sets the scrollbar parameters. first is the relative position of the top (left) of the display. last is the relative position of the bottom (right) of the display.
Top
Table of Contents
Top
Table of Contents
foreach field {Name Address1 Address2 Phone} { label .l$field -text $field -anchor w entry .e$field -textvariable address($field) -relief sunken grid .l$field .e$field -sticky news bind .e$field <Return> UpdateAddress } Example 31-1 creates four entries that are linked to variables with the textvariable attribute. The variables are elements of the address array. The -relief sunken for the entry widget sets them apart visually. Widget relief is described in more detail on page 530. The Tcl command UpdateAddress is
bound to the <Return> keystroke. The UpdateAddress procedure, which is not shown, can get the current values of the entry widgets through the global array address.
Top
Table of Contents
Sets the insert point and starts a selection. Drags out a selection. Selects a word. Selects all text in the entry. Adjusts the ends of the selection. Sets insert point, leaving selection as is. Pastes selection at the insert cursor. Scrolls horizontally. Moves insert cursor one character left and starts the selection. Moves cursor left and extends the selection. Moves cursor left one word and starts the selection. Same as <Control-Left>. Moves cursor left one word and extends the selection. Moves right one character. Moves right one word.
<Home> <Control-a> <Shift-Home> <End> <Control-e> <Shift-End> <Select> <Control-Space> <Shift-Select> <Control-ShiftSpace> <Control-slash> <Control-backslash> <Delete> <Backspace> <Control-h> <Control-d> <Meta-d> <Control-k> <Control-w> <Control-x> <Control-t>
Moves cursor to beginning of entry. Moves cursor to beginning and extends the selection. Moves cursor to end of entry. Moves cursor to end and extends the selection. Anchors the selection at the insert cursor. Adjusts the selection to the insert cursor. Selects all the text in the entry. Clears the selection in the entry. Deletes the selection or deletes next character. Deletes the selection or deletes previous character. Deletes next character. Deletes next word. Deletes to the end of the entry. Deletes previous word. Deletes the section, if it exists. Transposes characters.
Entry Attributes
Table 31-2 lists the entry widget attributes. The table lists the resource name, which has capitals at internal word boundaries. In Tcl commands these options are specified with a dash and are all lowercase.
background borderWidth cursor exportSelection font foreground highlightBackground highlightColor highlightThickness insertBackground insertBorderWidth insertOffTime insertOnTime insertWidth justify relief selectBackground selectForeground selectBorderWidth show state takeFocus textVariable width xScrollCommand
Background color (also bg). Extra space around the edge of the text (also bd). Cursor to display when mouse is over the widget. If true, selected text is exported via the X selection mechanism. Font for the text. Foreground color (also fg). Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle. Background for area covered by insert cursor. Width of cursor border. Non-zero for 3D effect. Time, in milliseconds the insert cursor blinks off. Time, in milliseconds the insert cursor blinks on. Width of insert cursor. Default is 2. Text justification: left, right, center.
flat, sunken, raised, groove, solid
or ridge.
Background color of selection. Foreground color of selection. Width of selection border. Nonzero for 3D effect. A character (e.g., *) to display instead of contents. State: disabled (read-only) or normal. Controls focus changes from keyboard traversal. Name of Tcl variable. Width, in characters. Connects entry to a scrollbar.
Index of the first character. The index of the anchor point of the selection. Index just after the last character. Index a character, counting from zero. The character right after the insertion cursor. The first character in the selection. The character just after the last character in the selection. The character under the specified X coordinate.
Table 31-4 summarizes the operations on entry widgets. In the table, $w is an entry widget.
Returns the value of the configuration option. Queries or modifies the widget configuration. Deletes the characters from first to last, not including the character at last. The character at first is deleted if last is not specified. Returns the string in the entry. Moves the insert cursor. Returns the numerical index corresponding to index. Inserts the string at the given index. Starts a scroll operation. x is a screen coordinate. Scrolls from previous mark position. Moves the boundary of an existing selection. Clears the selection. Sets the anchor position for the selection. Returns 1 if there is a selection in the entry. Selects the characters from start to the one just before end.
Extends the selection. Returns the offset and span of visible contents. These are both real numbers between 0 and 1.0. Shifts the display so the character at index is at the left edge of the display. Shifts the display so that fraction of the contents are off the left edge of the display. Scrolls the contents by the specified number of what, which can be units or pages.
For example, the binding for <Button-1> includes the following commands: %W icursor @%x %W select from @%x if {%W cget -state] == "normal"} {focus %W} Recall that the % triggers substitutions in binding commands, and that %W is replaced with the widget pathname and %x is replaced with the X coordinate of the mouse event. Chapter 26 describes bindings and these substitutions in detail. These commands set the insert point to the point of the mouse click by using the @%x index, which will be turned into something like @17 when the binding is invoked. The binding also starts a selection. If the entry is not in the disabled state, then keyboard focus is given to the entry so that it gets KeyPress events.
Top
Table of Contents
Top
Table of Contents
Using Listboxes
The lines in a listbox are indexed from zero. The keyword index end addresses the last line. Other indices are described on page 445. The most common programming task for a listbox is to insert text. If your data is in a list, you can loop through the list and insert each element at the end: foreach item $list { $listbox insert end $item } You can insert several items at once. The next command uses eval to concatenate the list onto a single insert command: eval {$listbox insert end}$list It is also common to react to mouse clicks on a listbox, although the default bindings handle most of the details of selecting items. The nearest operation finds the listbox entry that is closest to a mouse event. If the mouse is clicked beyond the last element, the index of the last element is returned: set index [$list nearest $y] Example 32-1 displays two listboxes. The Scrolled_Listbox procedure on page 432 is used to put scrollbars on the listboxes. When the user clicks on an item in the first listbox, it is copied into the second listbox. When an item in the second listbox is selected, it is removed. This example shows how to manipulate items selected from a listbox: Example 32-1 Choosing items from a listbox.
proc List_Select { parent values } { # Create two lists side by side frame $parent set choices [Scrolled_Listbox $parent.choices \ -width 20 -height 5 ] set picked [Scrolled_Listbox $parent.picked \ -width 20 -height 5] pack $parent.choices $parent.picked -side left \ -expand true -fill both # Selecting in choices moves items into picked bind $choices <ButtonRelease-1> \ [list ListTransferSel %W $picked] # Selecting in picked deletes items bind $picked <ButtonRelease-1> \ {ListDeleteSel %W %y} # Insert all the choices foreach x $values { $choices insert end $x } } proc ListTransferSel {src dst} { foreach i [$src curselection] { $dst insert end [$src get $i] } } proc ListDeleteSel {w y} { foreach i [lsort -integer -decreasing [$w curselection]] { $w delete $i } } proc List_SelectValues {parent} { set picked $parent.picked.list set result {} foreach i [$w curselection] { lappend $result [$w get $i] } }
List_Select .f {apples oranges bananas \ grapes mangos peaches pears} pack .f -expand true -fill both Bindings are created to move items from $choices to $picked, and to delete items from $picked. Most of the work of selecting things in the listbox is done by the built-in bindings on the Listbox binding tag. The different selection models are described on page 448. Those bindings are on <ButtonPress-1> and <B1-Motion>. The selection is complete by the time the <ButtonRelease-1> event occurs. Consider the <ButtonRelease-1> binding for $choices: bind $choices <ButtonRelease-1> \ [list ListTransferSel %W $picked] The list command is used to construct the Tcl command because we need to expand the value of $picked at the time the binding is created. The command will be evaluated later at the global scope, and picked will not be defined after the List_Select procedure returns. Or, worse yet, an existing global variable named picked will be used, which is unlikely to be correct! Short procedures are used to implement the binding commands. This style has two advantages. First, it confines the % substitutions done by bind to a single command. Second, if there are any temporary variables, such as the loop counter i, they are hidden within the scope of the procedure. The ListTransferSel gets the list of all the selected items and loops over this list to insert them into the other list. The ListDeleteSel procedure is similar. However, it sorts the selection indices in reverse order. It deletes items from the bottom up so the indices remain valid throughout the process.
Programming Listboxes
The listbox operations use indices to reference lines in the listbox. The lines are numbered starting at zero. Keyword indices are also used for some special lines. The listbox keeps track of an active element, which is displayed with underlined text. There is also a selection anchor that is used when adjusting selections. Table 32-1 summarizes the keywords used for indices.
Index of the first line. The index of the activated line. The index of the anchor point of the selection. Index of the last line. Index a line, counting from zero. The line closest to the specified X and Y coordinates.
Table 32-2 presents the operations for programming a listbox. In the table, $w is a listbox widget. Most of the operations have to do with the selection, and these operations are already programmed by the default bindings for the Listbox widget class:
Activates the specified line. Returns the bounding box of the text in the specified line in the form: xoff yoff width height. Returns the value of the configuration option. Queries or modifies the widget configuration. Returns a list of indices of the selected lines. Deletes the lines from first to last, including the line at last. The line at first is deleted if last is not given. Returns the lines from first to last as a list. Returns the numerical index corresponding to index. Inserts the string items before the line at index. If index is end, then append the items. Returns the index of the line closest to the widget-relative Y coordinate. Starts a scroll operation. x and y are widget-relative screen coordinates. Scrolls from previous mark position. Adjusts the display so the line at index is visible. Anchors the selection at the specified line. Clears the selection. Returns 1 if the line at index is in the selection. Selects the lines from start to end. Returns the offset and span of visible contents. These are both real numbers between 0 and 1. Shifts the display so the character at index is at the left edge of the display. Shifts the display so that fraction of the contents are off the left edge of the display.
Scrolls the contents horizontally by the specified number of what, which can be units or pages. Returns the offset and span of visible contents. These are both real numbers between 0 and 1. Shifts the display so the line at index is at the top edge of the display. Shifts the display so that fraction of the contents are off the top of the display. Scrolls the contents vertically by the specified number of what, which can be units or pages.
Top
Table of Contents
Listbox Bindings
A listbox has an active element and it may have one or more selected elements. The active element is highlighted with an underline, and the selected elements are highlighted with a different color. There are a large number of key bindings for listboxes. You must set the input focus to the listbox for the key bindings to work. Chapter 36 describes focus. There are four selection modes for a listbox, and the bindings vary depending what mode the listbox is in. Table 32-3 lists the four possible selectMode settings:
A single element can be selected. A single element can be selected, and the selection can be dragged with the mouse. This is the default. More than one element can be selected by toggling the selection state of items, but you only select or deselect one line at a time. More than one element can be selected by dragging out a selection with the shift or control keys.
<Button-1> <B1-Motion> <Shift-Button-1> <Key-Up> <Key-Down> <Control-Home> <Control-End> <space> <Select> <Controlslash>
Selects the item under the mouse. This becomes the active element, too. Same as <Button-1>, the selection moves with the mouse. Activates the item under the mouse. The selection is not changed. Moves the active item up (down) one line, and selects it. Activates and select the first element of the listbox. Activates and select the last element of the listbox. Selects the active element.
Selects the item under the mouse. Activates the item under the mouse. Activates the item under the mouse. The selection is not changed. Moves the active item up (down) one line. The selection is not changed. Activates and selects the first element of the listbox. Activates and selects the last element of the listbox. Selects the active element. Clears the selection.
Selects the item under the mouse. This becomes the anchor point for adjusting the selection. Sweeps out a selection from the anchor point. Activates the item under the mouse. Adjusts the selection from the anchor item to the item under the mouse. Continues to adjust the selection from the anchor. Toggles the selection state of the item under the mouse, and makes this the anchor point. Sets the selection state of the items from the anchor point to the item under the mouse to be the same as the selection state of the anchor point. Moves the active item up (down) one line, and start a new selection with this item as the anchor point. Moves the active element up (down) and extends the selection to include this element. Activates and selects the first element of the listbox. Extends the selection to the first element. Activates and selects the last element of the listbox. Extends the selection to the last element. Selects the active element. Cancels the previous selection action. Selects everything in the listbox. Clears the selection.
<Button-1> <ButtonRelease-1> <Key-Up> <Key-Down> <Shift-Up> <ShiftDown> <Control-Home> <Control-Shift-Home> <Control-End> <Control-Shift-End> <space> <Select> <Control-slash> <Control-backslash>
Selects the item under the mouse. Activates the item under the mouse. Moves the active item up (down) one line, and starts a new selection with this item as the anchor point. Moves the active element up (down). Activates and selects the first element of the listbox. Activates the first element of the listbox. Activates and selects the last element of the listbox. Activates the last element of the listbox. Selects the active element. Selects everything in the listbox. Clears the selection.
Scroll Bindings
There are a number of bindings that scroll the display of the listbox. In addition to the standard middle-drag scrolling, there are some additional key bindings for scrolling. Table 32-8 summarizes the the scroll-related bindings:
Marks the start of a scroll operation. Scrolls vertically and horizontally. Scrolls horizontally by one character. Scrolls horizontally by one screen width. Scrolls vertically by one screen height. Scrolls to left and right edges of the screen, respectively.
Top
Table of Contents
Listbox Attributes
Table 32-9 lists the listbox widget attributes. The table uses the resource name for the attribute, which has capitals at internal word boundaries. In Tcl commands these options are specified with a dash and all lowercase.
Background color (also bg). Extra space around the edge of the text. Cursor to display when mouse is over the widget. If true, then the selected text is exported via the X selection mechanism. Font for the text. Foreground color (also fg). Number of lines in the listbox. Focus highlight color when widget does not have focus. Focus highlight color when widget has focus. Thickness of focus highlight rectangle.
flat, sunken, raised, groove, solid,
or ridge.
Background color of selection. Foreground color of selection. Width of selection border. Nonzero for 3D effect. Mode: browse, single, extended, or multiple. Boolean. Set gridding attribute.
Controls focus changes from keyboard traversal. Width, in average character sizes. Connects listbox to a horizontal scrollbar. Connects listbox to a vertical scrollbar.
Geometry Gridding
The setGrid attribute affects interactive resizing of the window containing the listbox. By default, a window can be resized to any size. If gridding is turned on, the size is restricted so that a whole number of lines and a whole number of average-width characters is displayed. Gridding affects the user feedback during an interactive resize. Without gridding the size is reported in pixel dimensions. When gridding is turned on, then the size is reported in grided units.
Top
Table of Contents
Top
Table of Contents
Text Indices
The characters in a text widget are addressed by their line number and the character position within the line. Lines are numbered starting at one, while characters are numbered starting at zero. The numbering for lines was chosen to be compatible with other programs that number lines starting at one, like compilers that generate line-oriented error messages. Here are some examples of text indices:
1.0 1.1 2.end
The first character. The second character on the first line. The newline character on the second line.
There are also symbolic indices. The insert index is the position at which new characters are normally inserted when the user types in characters. You can define new indices called marks, too, as described later. Table 33-1 summarizes the various forms for a text index.
line.char @x,y current end image insert mark tag.first tag.last window
Lines count from 1. Characters count from 0. The character under the specified screen position. The character currently under the mouse. Just after the very last character. The position of the embedded image. The position right after the insert cursor. Just after the named mark. The first character in the range tagged with tag. Just after the last character tagged with tag. The position of the embedded window.
Index Arithmetic
The text widget supports a simple sort of arithmetic on indices. You can specify "the end of the line with this index" and "three characters before this index," and so on. This is done by grouping a
modifying expression with the index. For example, the insert index can be modified like this: "insert lineend" "insert -3 chars" The interpretation of indices and their modifiers is designed to operate well with the delete and tag add operations of the text widget. These operations apply to a range of text defined by two indices. The second index refers to the character just after the end of the range. For example, the following command deletes the word containing the insert cursor: $t delete "insert wordstart" "insert wordend" If you want to delete a whole line, including the trailing newline, you need to use a "lineend +1 char" modifier. Otherwise, the newline remains and you are left with a blank line. If you supply several modifiers to an index, they are applied in left to right order: $t delete "insert linestart" "insert lineend +1 char" Table 33-2 summarizes the set of index modifiers.
lines past the index, retaining character position. lines before the index, retaining character position.
The beginning of the line. The end of the line (i.e., the newline character). The first character of a word. Just after the last character of a word.
Comparing Indices
The compare operation compares two text indices and index expressions. You must use compare for reliable comparisons because, for example, index 1.3 is less than index 1.13. If you try to compare indices as numbers, you get the wrong answer. The general form of the compare operation is:
$t compare ix1 op ix2 The comparison operator can be one of <, <=, ==, =>, >, or !=. The indices can be simple indices in the forms listed in Table 33-1, and they can be index expressions. Example 33-6 on page 467 uses the compare operation.
Top
Table of Contents
Text Marks
A mark is a symbolic name for a position between two characters. Marks have the property that when text is inserted or deleted they retain their logical position, not their numerical index position. Marks are persistent: If you delete the text surrounding a mark, it remains intact. Marks are created with the mark set operation and must be explicitly deleted with the mark unset operation. Once defined, a mark can be used in operations that require indices. The following commands define a mark at the beginning of the word containing the insert cursor and delete from there up to the end of the line: $t mark set foobar "insert wordstart" $t delete foobar "foobar lineend" $t mark unset foobar When a mark is defined, it is set to be just before the character specified by the index expression. In the previous example, this is just before the first character of the word where the insert cursor is. When a mark is used in an operation that requires an index, it refers to the character just after the mark. So, in many ways the mark seems associated with the character right after it, except that the mark remains even if that character is deleted. You can use almost any string for the name of a mark. However, do not use pure numbers and do not include spaces, plus (+) or minus (-). These characters are used in the mark arithmetic and may cause problems if you put them into mark names. The mark names operation returns a list of all defined marks. The insert mark defines where the insert cursor is displayed. The insert mark is treated specially: you cannot remove it with the mark unset operation. Attempting to do so does not raise an error, though, so the following is a quick way to unset all marks. The eval is necessary to join the list of mark names into the mark unset command: eval {$t mark unset}[$t mark names]
Mark Gravity
Each mark has a gravity that determines what happens when characters are inserted at the mark. The default gravity is right, which means that the mark sticks to the character that is to its right. Inserting text at a mark with right gravity causes the mark to be pushed along so it is always after the inserted text. With left gravity the mark stays with the character to its left, so inserted text goes after the mark and the mark does not move. In versions of Tk before 4.0, marks had only right gravity, which made some uses of marks awkward. The mark gravity operation is used to query and modify the gravity of a mark: $t mark gravity foobar => right $t mark gravity foobar left
Top
Table of Contents
Text Tags
A tag is a symbolic name that is associated with one or more ranges of characters. A tag has attributes that affect the display of text that is tagged with it. These attributes include fonts, colors, tab stops, line spacing and justification. A tag can have event bindings so you can create hypertext. A tag can also be used to represent application-specific information. The tag names and tag ranges operations described later tell you what tags are defined and where they are applied. You can use almost any string for the name of a tag. However, do not use pure numbers, and do not include spaces, plus (+) or minus (-). These characters are used in the mark arithmetic and may cause problems if you use them in tag names. A tag is added to a range with the tag add operation. The following command applies the tag everywhere to all the text in the widget: $t tag add everywhere 1.0 end You can add one or more tags when text is inserted, too: $t insert insert "new text" {someTag someOtherTag} If you do not specify tags when text is inserted, then the text picks up any tags that are present on the characters on both sides of the insertion point. (Before Tk 4.0, tags from the left-hand character were picked up.) If you specify tags in the insert operation, only those tags are applied to the text. A tag is removed from a range of text with the tag remove operation. However, even if there is no text labeled with a tag, its attribute settings are remembered. All information about a tag can be removed with the tag delete operation: $t tag remove everywhere 3.0 6.end $t tag delete everywhere
Tag Attributes
The attributes for a tag are defined with the tag configure operation. For example, a tag for blue text is defined with the following command: $t tag configure blue -foreground blue Table 33-3 specifies the set of attributes for tags. Some attributes can only be applied with tags; there is no global attribute for -bgstipple, -fgstipple, -justify, -lmargin1, -lmargin2, -offset, overstrike, -rmargin, and -underline. Table 33-10 on page 474 lists the attributes for the text widget as a whole. The -relief and -borderwidth attributes go together. If you only specify a relief, there is no visible effect. The default relief is flat, too, so if you specify a border width without a relief you won't see any effect either. The stipple attributes require a bitmap argument. Bitmaps and colors are explained in more detail in Chapter 38. For example, to "grey out" text you could use a foreground stipple of gray50: $t tag configure disabled -fgstipple gray50
The background color for text. A stipple pattern for the background color. The width for 3D border effects. A stipple pattern for the foreground color. The font for the text. The foreground color for text. Justification: left, right, or center. Normal left indent for a line. Indent for the part of a line that gets wrapped. Baseline offset. Positive for superscripts. Draw text with a horizontal line through it.
flat, sunken, raised, groove, solid
or ridge.
-spacing2 pixels -spacing3 pixels -tabs tabstops -underline boolean -wrap mode
Additional space above wrapped part of line. Additional space below a line. Specifies tab stops. If true, the text is underlined. Line wrap: none, char, or word.
You can set up the appearance (and bindings) for tags once in your application, even before you have labeled any text with the tags. The attributes are retained until you explicitly delete the tag. If you are going to use the same appearance over and over again, then it is more efficient to do the setup once so that Tk can retain the graphics context. On the other hand, if you change the configuration of a tag, any text with that tag will be redrawn with the new attributes. Similarly, if you change a binding on a tag, all tagged characters are affected immediately. Example 33-1 defines a few tags for character styles you might see in an editor. The example is uses the font naming system added in Tk 8.0, which is described on page 550. Example 33-1 Tag configurations for basic character styles. proc TextStyles { t $t tag configure $t tag configure $t tag configure $t tag configure $t tag configure $t tag configure } } { bold -font {times 12 bold} italic -font {times 12 italic} fixed -font {courier 12} underline -underline true super -offset 6 -font {helvetica 8} sub -offset -6 -font {helvetica 8}
marked for delete, and it can use another color for messages that are marked to be moved into another folder. The tags might be defined like this: $t tag configure deleted -background grey75 $t tag configure moved -background yellow These tags conflict, but they are never used on the same message. However, a selection could be indicated with an underline, for example: $t tag configure select -underline true You can add and remove the select tag to indicate what messages have been selected, and the underline is independent of the background color determined by the moved or deleted tag. If you look at the exmh implementation, the ftocColor.tcl file defines several text tags that are composed like this.
proc TextExample { f } { frame $f pack $f -side top -fill both -expand true set t [text $f.t -setgrid true -wrap word \ -width 42 -height 14 \ -yscrollcommand "$f.sy set"] scrollbar $f.sy -orient vert -command "$f.t yview" pack $f.sy -side right -fill y pack $f.t -side left -fill both -expand true $t tag configure para -spacing1 0.25i -spacing2 0.1i \ -lmargin1 0.5i -lmargin2 0.1i -rmargin 0.5i $t tag configure hang -lmargin1 0.1i -lmargin2 0.5i $t insert end "Here is a line with no special settings\n" $t insert end "Now is the time for all good women and men to come to the aid of their country. In this great time of need, no one can avoid their responsibility.\n" $t insert end "The quick brown fox jumps over the lazy dog." $t tag add para 2.0 2.end $t tag add hang 3.0 3.end } The example defines two tags, para and hang, that have different spacing and margins. The spacing1 setting for para causes the white space before the second line. The -spacing2 setting causes the white space between the wrapped portions of the second paragraph. The hang tag has no spacing attributes, so the last paragraph starts right below the previous paragraph. You can also see the difference between the -lmargin1 and -lmargin2 settings. The newline characters are inserted explicitly. Each newline character defines a new line for the purposes of indexing, but not necessarily for display, as this example shows. In the third line there is no newline. This means that if more text is inserted at the end mark, it will be on line three. The values for the spacing and margin parameters are in screen units. Because different fonts are
different sizes, you may need to compute the spacings as a function of the character sizes. The bbox operation returns the bounding box (x, y, width, height) for a given character: $t insert 1.0 "ABCDE" $t bbox 1.0 => 4 4 8 12 The Tk 8.0 font metrics command, which is described on page 554, also gives detailed measurements: font metrics {times 12} -ascent 9 -descent 3 -linespace 12 -fixed 0 Text justification is limited to three styles: left, right, or center. There is no setting that causes the text to line up on both margins, which would have to be achieved by introducing variable spacing between words.
Tab Stops
Text widgets have adjustable tab stops. The tabs attribute is a list of tab stops, which are specified with a screen unit and optionally a keyword that indicates justification. The tab justification keywords are left, right, center, and numeric, and these can be abbreviated. The default is left. The following resource specification defines tab stops at 2-centimeter intervals with different justification: *Text.tabs: 2c left 4c right 6c center 8c numeric The tabs attribute applies to the whole text widget or to a tag. The last tab stop is extrapolated as needed. The following command defines a tag that has left justified tab stops every half inch: $t tag configure foo -tabs ".5i left"
Top
Table of Contents
The Selection
The selection is implemented with a predefined tag named sel. If the application tags characters with sel, those characters are added to the selection. This is done as part of the default bindings on the text widget. The exportSelection attribute of a text widget controls whether or not selected text is exported by the selection mechanism to other applications. By default the selection is exported. In this case, when another widget or application asserts ownership of the selection then the sel tag is removed from any characters that are tagged with it. Chapter 35 describes the selection mechanism in more detail. You cannot delete the sel tag with the tag delete operation. However, it is not an error to do so. You can delete all the tags on the text widget with the following command. The eval command is used to join the list of tag names into the tag delete command: eval {$t tag delete}[$t tag names]
Top
Table of Contents
Tag Bindings
You can associate a tag with bindings so that when the user clicks on different areas of the text display, different things happen. The syntax for the tag bind command is similar to that of the main Tk bind command. You can both query and set the bindings for a tag. Chapter 26 describes the bind command and the syntax for events in detail. The only events supported by the tag bind command are Enter, Leave, ButtonPress, ButtonRelease, Motion, KeyPress, and KeyRelease. ButtonPress and KeyPress can be shorted to Button and Key as in the regular bind command. The Enter and Leave events are triggered when the mouse moves in and out of characters with a tag, which is different from when the mouse moves in and out of the window. If a character has multiple tags, then the bindings associated with all the tags will be invoked, in the order from lowest priority tag to highest priority tag. After all the tag bindings have run, the binding associated with the main widget is run, if any. The continue and break commands work inside tag bindings in a similar fashion as they work with regular command bindings. See Chapter 26 for the details. Example 33-3 defines a text button that has a highlighted relief and an action associated with it. The example generates a new tag name so that each text button is unique. The relief and background are set for the tag to set it apart visually. The winfo visual command is used to find out if the display supports color before adding a colored background to the tag. On a black and white display, the button is displayed in reverse video (i.e., white on black.) The command is bound to <Button-1>, which is the same as <ButtonPress-1>. The cursor is changed when the mouse is over the tagged area by binding to the <Enter> and <Leave> events. Upon leaving the tagged area, the cursor is restored. Another tag is used to remember the previous setting for the cursor. You could also use a global variable, but it is often useful to decorate the text with tags for your own purposes. Example 33-3 An active text button. proc TextButton { t start end command } { global textbutton if ![info exists textbutton(uid)] {
set textbutton(uid) 0 } else { incr textbutton(uid) } set tag button$textbutton(uid) $t tag configure $tag -relief raised -borderwidth 2 if {[regexp color [winfo visual $t]]} { $t tag configure $tag -background thistle } else { $t tag configure $tag -background [$t cget -fg] $t tag configure $tag -foreground [$t cget -bg] } # Bind the command to the tag $t tag bind $tag <Button-1> $command $t tag add $tag $start $end # use another tag to remember the cursor $t tag bind $tag <Enter> \ [list TextButtonChangeCursor %W $start $end tcross] $t tag bind $tag <Leave> {TextButtonRestoreCursor %W} } proc TextButtonChangeCursor {t start end cursor} { $t tag add cursor=[$t cget -cursor] $start $end $t config -cursor $cursor } proc TextButtonRestoreCursor {t} { regexp {cursor=([^ ]*)}[$t tag names] x cursor $t config -cursor $cursor } To behave even more like a button, the action should trigger upon <ButtonRelease-1>, and the appearance should change upon <ButtonPress-1>. If this is important to you, you can always embed a real Tk button. Embedding widgets is described later.
Top
Table of Contents
Searching Text
The search operation scans the text widget for a string that matches a pattern. The index of the text that matches the pattern is returned. The search starts at an index and covers all the text widget unless a stop index is supplied. You can use end as the stop index to prevent the search from wrapping back to the beginning of the document. The general form of the search operation is this: $t search ?options? pattern index ?stopIndex? Table 33-4 summarizes the options to the search operation:
Searches forward from index. This is the default. Searches backward from index. Matches pattern exactly. This is the default. Uses regular expression pattern matching. Lowercase letters in pattern can match upper case letters. Returns in varName the number of characters that matched pattern. Ends the options. Necessary if pattern begins with -.
If you use a regular expression to match a pattern, you may be interested in how much text matched so you can highlight the match. The -count option specifies a variable that gets the number of matching characters: set start [$t search -count cnt -regexp -- $pattern 1.0 end] $t tag add sel $start "$start +$cnt chars"
Top
Table of Contents
Embedded Widgets
The text widget can display embedded widgets as well as text. You can include a picture, for example, by constructing it in a canvas and then inserting the canvas into the text widget. An embedded widget takes up one character in terms of indices. You can address the widget by its index position or by the Tk pathname of the widget. For example, suppose $t names a text widget. The following commands create a button and insert it into the text widget. The button behaves normally, and in this case it invokes the Help command when the user clicks on it: button $t.help -bitmap questhead -command Help $t window create end -window $t.help By default an embedded widget is centered vertically on its text line. You can adjust this with the align option to the window create command. This setting only takes effect if the window is smaller than the text in the line. I find that windows are usually larger than the text line, and in that case the align setting has no effect. This setting is also used with images, however, where it is more common to have small images (e.g., for special bullets). Table 33-5 describes the window and image alignment settings:
Top lines up with top of text line. Center lines up with center of text line. Bottom lines up with text baseline. Bottom lines up with bottom of text line.
You can postpone the creation of the embedded widget by specifying a Tcl command that creates the window, instead of specifying the -window option. The delayed creation is useful if you have lots of
widgets embedded in your text. In this case the Tcl command is evaluated just before the text widget needs to display the widget. In other words, when the user scrolls the text so the widget will appear, the Tcl command is run to create the widget: Example 33-4 Delayed creation of embedded widgets. $t window create end -create [list MakeGoBack $t] proc MakeGoBack { t } { button $t.goback -text "Go to Line 1" \ -command [list $t see 1.0] } The MakeGoBack procedure is introduced to eliminate potential quoting problems. If you need to execute more than one Tcl command to create the widget or if the embedded button has a complex command, the quoting can quickly get out of hand. Table 33-6 gives the complete set of options for creating embedded widgets. You can change these later with the window configure operation. For example: $t window configure $t.goback -padx 2
Alignment: top, center, bottom, or baseline. Tcl command to create the widget. Padding on either side of the widget. Padding above and below the widget. If true, the widget is stretched vertically to match the spacing of the text line. Tk pathname of the widget to embed.
You can specify the window to reconfigure by its pathname or the index where the window is located. In practice, naming the widget by its pathname is much more useful. Note that end is not useful for identifying an embedded window because the text widget treats end specially. You can insert a window at end, but end is always updated to be after the last item in the widget. Thus end will never name the position of an existing window.
Top
Table of Contents
Embedded Images
Tk 8.0 added embedded images that are much like embedded windows. They provide a more efficient way to add images than creating a canvas or label widget to hold the image. You can also put the same image into a text widget many times. Example 33-5 uses an image for the bullets in a bulleted list: Example 33-5 Using embedded images for a bulleted list. proc BList_Setup { t imagefile } { global blist set blist(image) [image create photo -file $imagefile] $t tag configure bulletlist -tabs ".5c center 1c left" \ -lmargin1 0 -lmargin2 1c } proc BList_Item {t text {mark insert}} { global blist # Assume we are at the beginning of the line $t insert $mark \t bulletlist $t image create $mark -image $blist(image) $t insert $mark \t$text bulletlist } In Example 33-5, tabs are used to line up the bullet and the left edges of the text. The first tab centers the bullet over a point 0.5 centimeters from left margin. The second tab stop is the same as the lmargin2 setting so the text on the first line lines up with the text that wraps onto more lines. If you update the image dynamically, all the instances of that image in the text widget are updated, too. This follows from the image model used in Tk, which is described in Chapter 38 on page 541. The options for embedded images are mostly the same as those for embedded windows. One difference is that images have a -name option so you can reference an image without remembering its position in the text widget. You cannot use the image name directly because the same image can be embedded many times in the text widget. If you do not choose a name, the text widget assigns a name for you. The image create operation returns this name:
$t => $t =>
image create 1.0 -image image1 image1 image create end -image image1 image1#1
Table 33-7 gives the complete set of options for creating embedded images. You can change these later with the image configure operation.
Alignment: top, center, bottom, or baseline. Only has effect if image is shorter than the line height. See Table 33-5. The Tk image to add to the text widget. A name for this instance of the image. A #num may be appended to generate a unique name. Padding on either side of the image. Padding above and below the image.
Top
Table of Contents
Looking at Tags
The tag names command returns all the tag names, or the names of the tags at a specified index: $t tag names ?index? A text tag can be applied to many different ranges of text. The tag ranges operation returns a list of indices that alternate between the start and end of tag ranges. The foreach command with two loop variables makes it easy to iterate through all the ranges: foreach {start end}[$t tag ranges $tag] { # start is the beginning of a range # end is the end of a range } The tag nextrange and tag prevrange operations return two indices that delimit the next and previous range of a tag. They take a starting index and an optional ending index. The tag nextrange operation skips to the next range if the tag is present at the starting index, unless the starting index is right at the start of a range. The tag prevrange operation is complementary. It does not skip the current range, unless the starting index is at the beginning of the range. These rules are used in Example 33-6 that defines a procedure to return the current range: Example 33-6 Finding the current range of a text tag.
proc Text_CurrentRange { t tag mark } { set range [$t tag prevrange $tag $mark] set end [lindex $range 1] if {[llength $range] == 0 || [$t compare $end < $mark]} { # This occurs when the mark is at the # very beginning of the node set range [$t tag nextrange $tag $mark] if {[llength $range] == 0 || [$t compare $mark < [lindex $range 0]]} { return {} } } return $range }
Looking at Marks
The mark names operation returns the names of all the marks. Unlike tag names, you cannot supply an index to find out if there are marks there. You must use the dump operation described later. The mark next and mark previous operations search from a given index for a mark. The mark next operation will find a mark if it is at the starting index.
foreach {key value index}[$t dump $start $end] { if {$key == "text"} { puts "$index \"$value\"" } else { puts "$index $key $value" } } } Instead of having dump return all the information, you can have it call a Tcl command to process each element. The command gets passed three pieces of information for each element: the type, the value, and the index. Example 33-8 shows another way to print out the text widget contents: Example 33-8 Dumping the text widget with a command callback. proc Text_Dump {t {start 1.0} {end end}} { $t dump -command TextDump $start $end } proc TextDump {key value index} { if {$key == "text"} { puts "$index \"$value\"" } else { puts "$index $key $value" } }
Top
Table of Contents
Text Bindings
There is an extensive set of default bindings for text widgets. In general, the commands that move the insertion cursor also clear the selection. Often you can hold the Shift key down to extend the selection, or hold the Control key down to move the insertion cursor without affecting the selection. Table 33-8 lists the default bindings for the text widget:
Inserts normal printing characters. Sets the insert point, clears the selection, sets focus. Sets the insert point without affecting the selection. Sweeps out a selection from the insert point. Selects the word under the mouse. Selects the line under the mouse. Adjusts the end of selection closest to the mouse. Continues to adjust the selection. Pastes the selection, or set the scrolling anchor. Scrolls the window. Moves the cursor left one character and clears the selection. Moves the cursor and extend the selection. Moves the cursor by words. Clears the selection. Moves the cursor by words. Extends selection.
Right
<Meta-b> <Meta-f> <Key-Up> <Control-p> <Shift-Up> <Control-Up> <Control-Shift-Up> <Key-Down> <Control-n> <Next> <Prior> <Shift-Next> <Shift-Prior> <Home> <Control-a> <Shift-Home> <End> <Control-e> <Shift-End> <Control-Home> <Meta-less> <Control-End> <Metagreater> <Select> <Control-space> <Shift-Select> <ControlShift-space> <Control-slash> <Control-backslash> <Delete> <BackSpace> <Control-h> <Control-d> <Meta-d> <Control-k> <Control-o> <Meta-Delete> <MetaBackSpace> <Control-t> <<Copy>> <Meta-w>
Same as <Control-Left>, <Control-Right>. Moves the cursor up one line. Clears the selection. Moves the cursor up one line. Extends the selection. Moves the cursor up by paragraphs, which are a group of lines separated by a blank line. Moves the cursor up by paragraph. Extends the selection. All Down bindings are analogous to Up bindings. Moves the cursor by one screen. Clears the selection. Moves the cursor by one screen. Extends the selection. Moves the cursor to line start. Clears the selection. Moves the cursor to line start. Extends the selection. Moves the cursor to line end. Clears the selection. Moves the cursor to line end. Extends the selection. Moves the cursor to the beginning of text. Clears the selection. Moves the cursor to the end of text. Clears the selection. Sets the selection anchor to the position of the cursor. Adjusts the selection to the position of the cursor. Selects everything in the text widget. Clears the selection. Deletes the selection, if any. Otherwise deletes the character to the right of the cursor. Deletes the selection, if any. Otherwise deletes the character to the left of the cursor. Deletes character to the right of the cursor. Deletes word to the right of the cursor. Deletes from cursor to end of the line. If you are at the end of line, delete the newline character. Inserts a newline but does not advance the cursor. Deletes the word to the left of the cursor. Transposes the characters on either side of the cursor. Copies the selection to the clipboard.
Cuts the selection and saves it on the clipboard. Pastes from the clipboard.
Top
Table of Contents
Text Operations
Table 33-9 describes the text widget operations, including some that are not discussed in this chapter. In the table, $t is a text widget:
Returns the bounding box of the character at index. Four numbers are returned: x y width height. Returns the value of the configuration option. Performs index comparison. i1 and i2 are indexes. op is one of <
<= == >= > !=
Queries or sets configuration options. Enables consistency checking for B-tree code. Deletes from i1 up to, but not including i2. Just deletes the character at i1 if i2 is not specified. Returns the bounding box, in pixels, of the display for the line containing index. Five numbers are returned: x y width height baseline. Returns the marks, tags, windows, images, and text contained in the widget. Options are -all, -command command, -image, -mark, -tag, -text, and -window . Returns the text from i1 to i2, or just the character at i1 if i2 is not specified. Returns the value of the image option. Queries or sets the configuration of an embedded image.
$t image create option value ... $t image names $t index index $t insert index chars ? tags? ?chars tags? ... $t mark gravity name ? direction? $t mark names $t mark next index $t mark previous index $t mark set name index $t mark unset name1 ? name2 ...? $t scan mark x y $t scan dragto x y $t search ?switches? pattern index ? stopIndex? $t see index $t tag add name i1 ?i2? ?i1 i2? ?i1 i2? ... $t tag bind name ? sequence? ?script? $t tag configure name ... $t tag cget name option $t tag delete tag1 ?tag2 ...? $t tag lower tag ?below? $t tag names ?index?
Creates an embedded image. Options are described in Table 33-7 on page 467. Returns the names of all embedded images. Returns the numerical value of index. Inserts chars at the specified index. If tags are specified, they are added to the new characters. Queries or assigns a gravity direction to the mark name. direction, if specified, is left or right. Returns a list of defined marks. Returns the mark after index. Returns the mark before index. Defines a mark name at the given index. Deletes the named mark, or marks. Anchors a scrolling operation. Scrolls based on a new position. Searches for pattern starting at index. The index of the start of the match is returned. Switches are described in Table 33-4 on page 464. Positions the display to view index. Adds the tag to i1 through, but not including i2, or just the character at i1 if i2 is not given. Queries or defines bindings for the tag name. Sets or queries the configuration of tag name. Returns the value of option for tag name. Deletes information for the named tags. Lowers the priority of tag to the lowest priority or to just below tag below. Returns the names of the tags at the specified index, or in the whole widget, sorted from lowest to highest priority. Returns a list of two indices that are the next range of text with tag that starts at or after i1 and before index i2, or the end. Returns a list of two indices that are the previous range of text with tag that ends at or before i1 and at or after index i2, or 1.0.
Raises the priority of tag to the highest priority, or to just above the priority of tag above. Returns a list describing all the ranges of tag. up to, but not including i2, or just at
$t tag remove tag i1 ?i2? Removes tag from the range i1 ?i1 i2? ?i1 i2? ... i1 if i2 is not specified. $t window config win ... $t window cget win option $t window create ix args $t window names $t xview
Queries or modifies the configuration of the embedded window. win is a Tk pathname or an index. Returns the value of option for win. Creates an embedded window at ix. Returns a list of windows embedded in $t. Returns two fractions between zero and one that describe the amount of text off-screen to the left and the amount of text displayed. Positions the text so fraction of the text is off screen to the left. Scrolls num of what, which is units or pages. Returns two fractions between zero and one that describe the amount of text off-screen toward the beginning and the amount of text displayed. Positions the text so fraction of the text is off-screen toward the beginning. Scrolls num of what, which is units or pages. Obsolete. Use the see operation, which is similar. Obsolete. Position line num at the top of screen.
$t yview moveto fraction $t yview scroll num what $t yview ?-pickplace? ix $t yview num
Top
Table of Contents
Text Attributes
Table 33-10 lists the attributes for the text widget. The table uses the resource name, which has capitals at internal word boundaries. In Tcl commands, the attributes are specified with a dash and all lowercase:
Background color (also bg). Extra space around the edge of the text. Cursor to display when mouse is over the widget. If true, selected text is exported to the selection. Default font for the text. Foreground color (also fg). Height, in text lines. Focus highlight color when widget does not have focus. Color for input focus highlight border. Width of highlight border. Color for the insert cursor. Size of 3D border for insert cursor. Milliseconds insert cursor blinks off. Milliseconds insert cursor blinks on. Width of the insert cursor. Extra space to the left and right of the text.
padY relief selectBackground selectForeground selectBorderWidth setGrid spacing1 spacing2 spacing3 state tabs takeFocus width wrap xScrollCommand yScrollCommand
or solid.
Background color of selected text. Foreground color of selected text. Size of 3D border for selection highlight. Enable/disable geometry gridding. Extra space above each unwrapped line. Space between parts of a line that have wrapped. Extra space below an unwrapped line. Editable (normal) or read-only (disabled). Tab stops. Control focus changes from keyboard traversal. Width, in characters, of the text display. Line wrap mode: none, char, or word. Tcl command prefix for horizontal scrolling. Tcl command prefix for vertical scrolling.
Top
Table of Contents
Top
Table of Contents
Canvas Coordinates
The coordinate space of the canvas has 0, 0 at the top left corner. Larger X coordinates are to the right, and larger Y coordinates are downward. The position and possibly the size of a canvas object is determined by a set of coordinates. Different objects are characterized by different numbers of coordinates. For example, text objects have two coordinates, x1 y1, that specify their anchor point. A line can have many pairs of coordinates that specify the end points of its segments. The coordinates are set when the object is created, and they can be updated later with the coords operation. By default, coordinates are in pixels. Append a coordinate with one of the following letters to change the units: c i m p centimeters inches millimeters printer points (1/72 inches)
The tk scale command, which is described on page 582, changes the mapping from pixels to other screen measures. Use it before creating the canvas. The width and height attributes of the canvas determine the size of the viewable area. The scrollRegion attribute of the canvas determines the boundaries of the canvas. Its value is four numbers that specify the upper-left and lower-right coordinates of the canvas. If you do not specify a scroll region, it defaults to the size of the viewable area. Example 34-1 creates a canvas that has a 1000 by 400 scrolling region, and a 300 by 200 viewing area. The canvas is connected to two scrollbars to provide horizontal and vertical scrolling: Example 34-1 A large scrolling canvas. proc Scrolled_Canvas { c args } { frame $c eval {canvas $c.canvas \ -xscrollcommand [list $c.xscroll set] \ -yscrollcommand [list $c.yscroll set] \ -highlightthickness 0 \
-borderwidth 0}$args scrollbar $c.xscroll -orient horizontal \ -command [list $c.canvas xview] scrollbar $c.yscroll -orient vertical \ -command [list $c.canvas yview] grid $c.canvas $c.yscroll -sticky news grid $c.xscroll -sticky ew grid rowconfigure $c 0 -weight 1 grid columnconfigure $c 0 -weight 1 return $c.canvas } Scrolled_Canvas .c -width 300 -height 200 \ -scrollregion {0 0 1000 400} => .c.canvas pack .c -fill both -expand true Borders are drawn in the canvas.
The highlight thickness and border width are set to 0 in Example 34-1. Otherwise, these features occupy some of the canvas viewable area. If you want a raised border for your canvas, either use another frame, or remember to offset your positions to avoid having objects clipped by the borders.
Top
Table of Contents
Hello, World!
Example 34-2 creates an object that you can drag around with the mouse. It introduces the use of tags to classify objects. In this case the movable tag gets bindings that let you drag the item, so any item with the movable tag shares this behavior. The example uses Scrolled_Canvas from Example 34-1. When you use a scrolled canvas, you must map from the view coordinates reported by bindings to the canvas coordinates used to locate objects: Example 34-2 The canvas "Hello, World!" example. proc CanvasHello {} { set can [Scrolled_Canvas .c -width 400 -height 100 \ -scrollregion {0 0 800 400}] pack .c -fill both -expand true # Create a text object on the canvas $can create text 50 50 -text "Hello, World!" -tag movable # Bind actions to objects with the movable tag $can bind movable <Button-1> {CanvasMark %x %y %W} $can bind movable <B1-Motion> {CanvasDrag %x %y %W} } proc CanvasMark {x y can} { global canvas # Map from view coordinates to canvas coordinates set x [$can canvasx $x] set y [$can canvasy $y] # Remember the object and its location set canvas($can,obj) [$can find closest $x $y] set canvas($can,x) $x set canvas($can,y) $y } proc CanvasDrag {x y can} { global canvas # Map from view coordinates to canvas coordinates set x [$can canvasx $x] set y [$can canvasy $y]
# Move the current object set dx [expr $x - $canvas($can,x)] set dy [expr $y - $canvas($can,y)] $can move $canvas($can,obj) $dx $dy set canvas($can,x) $x set canvas($can,y) $y } Example 34-2 creates a text object and gives it a tag named movable: .c create text 50 50 -text "Hello, World!" -tag movable The first argument after create specifies the type, and the remaining arguments depend on the type of object being created. Each canvas object requires some coordinates, optionally followed by attribute value pairs. The complete set of attributes for canvas objects are presented later in this chapter. A text object needs two coordinates for its location.
Canvas Tags
The create operation returns an ID for the object being created, which would have been 1 in this case. However, the code manipulates the canvas objects by specifying a tag instead of an object ID. A tag is a more general handle on canvas objects. Many objects can have the same tag, and an object can have more than one tag. You can define bindings on tags, and you can define attributes for tags that will be picked up by objects with those tags. A tag name can be almost any string, but you should avoid spaces that can cause parsing problems and pure numbers that get confused with object IDs. There are two predefined tags: current and all. The current tag applies to whatever object is under the mouse. The all tag applies to all the objects on the canvas. The example defines behavior for objects with the movable tag. Pressing button 1 starts a drag, and dragging with the mouse button down moves the object. The pathname of the canvas (%W) is passed to CanvasMark and CanvasDrag so these procedures can be used on different canvases. The %x and %y keywords get substituted with the X and Y coordinate of the event: $can bind movable <Button-1> {CanvasMark %x %y %W} $can bind movable <B1-Motion> {CanvasDrag %x %y %W} The CanvasMark and CanvasDrag procedures let you drag the object around the canvas. Because CanvasMark is applied to any object with the movable tag, it must first find the object that was clicked on. First, the view coordinates are mapped into the canvas coordinates with the canvasx and canvasy operations: set x [$can canvasx x] set y [$can canvasy y]
Once you do this, you can use the find operation: set canvas($can,obj) [$can find closest $x $y] The actual moving is done in CanvasDrag with the move operation: $can move $canvas($can,obj) $dx $dy Try creating a few other object types and dragging them around, too: $can create rect 10 10 30 30 -fill red -tag movable $can create line 1 1 40 40 90 60 -width 2 -tag movable $can create poly 1 1 40 40 90 60 -fill blue -tag movable The CanvasMark and CanvasDrag procedures can be used with any canvas. They use the global array canvas to keep their state, and they parameterize the indices with the canvas pathname to avoid conflict if there is more that one canvas in the application. If you get into this coding habit early, then you will find it easy to write reusable code. Canvas tags are not persistent.
Canvas tags do not work exactly like tags in the text widget. In the text widget, a tag is completely independent of the text. You can configure a text tag before it is applied to text, and the tag configuration is remembered even if you remove it from the text. A canvas tag, in contrast, must be applied to an object before you can configure it. If you configure a canvas tag that is not applied to any objects, those settings are forgotten. If you remove all the objects that share a tag, any settings associated with those tags are forgotten.
Top
Table of Contents
Many of the canvas operations take an argument that identifies objects. The value can be a tag name, or it can be the numerical object identifier returned by the create operation. Example 34-3 does not use object IDs. Instead, it gives each object a symbolic identifier with a tag, plus it introduces more tags to represent classes of objects. The example uses the all tag to move all the items and to find out the bounding box of the image. The left box and the left hanging text both have the left tag. They can be moved together, and they share the same bindings. Similarly, the right tag is shared by the right box and the right hanging text. Each item has its own unique tag, so it can be manipulated individually, too. Those tags are slider, lbox, lnum, rbox, and rnum: Example 34-3 A min max scale canvas example.
proc Scale2 {w min max {width {}}} { global scale2 if {$width == {}} { # Set the long dimension, in pixels set width [expr $max - $min] } # Save parameters set scale2($w,scale) [expr ($max-$min)/$width.0] set scale2($w,min) $min;# Current minimum set scale2($w,max) $max set scale2($w,Min) $min;# Lower bound to the scale set scale2($w,Max) $max set scale2($w,L) 10 set scale2($w,R) [expr $width+10] # # # # Build from 0 to 100, then scale and move it later. Distance between left edges of boxes is 100. The box is 10 wide, therefore the slider is 110 long. The left box sticks up, and the right one hangs down. 0 0 110 10 -fill grey -tag slider 0 -4 10 10 -fill black -tag {left lbox} 100 0 110 14 -fill red -tag {right rbox} 5 16 -anchor n -text $min -tag {left lnum} 105 16 -anchor n -text $max \ rnum}-fill red
canvas $w $w create rect $w create rect $w create rect $w create text $w create text -tag {right
# Stretch/shrink the slider to the right length set scale [expr ($width+10) / 110.0] $w scale slider 0 0 $scale 1.0 # move the right box and text to match new length set nx [lindex [$w coords slider] 2] $w move right [expr $nx-110] 0 # Move everything into view $w move all 10 10 # Make the canvas fit comfortably around the image set bbox [$w bbox all] set height [expr [lindex $bbox 3]+4] $w config -height $height -width [expr $width+30]
# Bind drag actions $w bind left <Button-1> {Scale2Mark %W %x lbox} $w bind right <Button-1> {Scale2Mark %W %x rbox} $w bind left <B1-Motion> {Scale2Drag %W %x lbox} $w bind right <B1-Motion> {Scale2Drag %W %x rbox} } The slider is constructed with absolute coordinates, and then it is scaled to the desired width. The alternative is to compute the coordinates based on the desired width. I have found it clearer to use numbers when creating the initial layout as opposed to using expr or introducing more variables. The scale operation stretches the slider bar to the correct length. The scale operation takes a reference point, which in our case is (0, 0), and independent scale factors for the X and Y dimensions. The scale factor is computed from the width parameter, taking into account the extra length added (10) so that the distance between the left edge of the slider boxes is $width: set scale [expr ($width+10) / 110.0] $w scale slider 0 0 $scale 1.0 The move operation repositions the right box and right hanging text. If the marker boxes are scaled, their shape gets distorted. The coords operation returns a list of four numbers: x1 y1 x2 y2. The distance to move is just the difference between the new right coordinate and the value used when constructing the slider initially. The box and text share the same tag, right, so they are both moved with a single move operation: set nx [lindex [$w coords slider] 2] $w move right [expr $nx-110] 0 After the slider is constructed, it is shifted away from (0, 0), which is the upper-left corner of the canvas. The bbox operation returns four coordinates: x1 y1 x2 y2, that define the bounding box of the items with the given tag. In the example, y1 is zero, so y2 gives us the height of the image. The information returned by bbox can be off by a few pixels, and the example needs a few more pixels of height to avoid clipping the text. The width is computed based on the extra length added for the marker box, the 10 pixels the whole image was shifted, and 10 more for the same amount of space on the right side: set bbox [$w bbox all] set height [expr [lindex $bbox 3]+4] $w config -height $height -width [expr $width+30] Bindings are defined for the box and hanging text. The general tags left and right are used for the bindings. This means that you can drag either the box or the text to move the slider. The pathname of the canvas is passed into these procedures so that you could have more than one double slider in your interface:
$w $w $w $w
<Button-1> {Scale2Mark %W %x lbox} <Button-1> {Scale2Mark %W %x rbox} <B1-Motion> {Scale2Drag %W %x lbox} <B1-Motion> {Scale2Drag %W %x rbox}
Example 34-4 Moving the markers for the min max scale. proc Scale2Mark {w x what } { global scale2 # Remember the anchor point for the drag set scale2($w,$what) $x } proc Scale2Drag { w x what } { global scale2 # Compute delta and update anchor point set x1 $scale2($w,$what) set scale2($w,$what) $x set dx [expr $x - $x1] # Find out where the boxes are currently set rx [lindex [$w coords rbox] 0] set lx [lindex [$w coords lbox] 0] if {$what == "lbox"} { # Constrain the movement to be between the # left edge and the right marker. if {$lx + $dx > $rx} { set dx [expr $rx - $lx] set scale2($w,$what) $rx } elseif {$lx + $dx < $scale2($w,L)} { set dx [expr $scale2($w,L) - $lx] set scale2($w,$what) $scale2($w,L) } $w move left $dx 0 # Update the minimum value and the hanging text set lx [lindex [$w coords lbox] 0] set scale2($w,min) [expr int($scale2($w,Min) + \ ($lx-$scale2($w,L)) * $scale2($w,scale))] $w itemconfigure lnum -text $scale2($w,min) } else { # Constrain the movement to be between the # right edge and the left marker if {$rx + $dx < $lx} { set dx [expr $lx - $rx] set scale2($w,$what) $lx } elseif {$rx + $dx > $scale2($w,R)} { set dx [expr $scale2($w,R) - $rx]
set scale2($w,$what) $scale2($w,R) } $w move right $dx 0 # Update the maximum value and the hanging text set rx [lindex [$w coords right] 0] set scale2($w,max) [expr int($scale2($w,Min) + \ ($rx-$scale2($w,L)) * $scale2($w,scale))] $w itemconfigure rnum -text $scale2($w,max) } } proc Scale2Value {w} { global scale2 # Return the current values of the double slider return [list $scale2($w,min) $scale2($w,max)] } The Scale2Mark procedure initializes an anchor position, scale2($w,$what), and Scale2Drag uses this to detect how far the mouse has moved. The change in position, dx, is constrained so that the markers cannot move outside their bounds. The anchor is updated if a constraint was used, and this means that the marker will not move until the mouse is moved back over the marker. (Try commenting out the assignments to scale2($w,$what) inside the if statement.) After the marker and hanging text are moved, the value of the associated parameter is computed based on the parameters of the scale. The Scale2Value procedure queries the current values of the double slider.
Top
Table of Contents
Canvas Objects
The next several sections describe the built-in object types for the canvas: arc, bitmap, image, line, oval, polygon , rectangle, text, and window. Each object has its own set of attributes, and some attributes are found on most or all object types. Every object has a -tags attribute used to label the object with a list of symbolic names. Most objects, even text, specify their color with the -fill attribute. Only the bitmap uses -foreground and -background. If the object has a border, the color of the border is specified with -outline, and the thickness of the outline is specified with -width.
Arc Items
An arc is a section of an oval. The dimensions of the oval are determined by four coordinates that are its bounding box. The arc is then determined by two angles, the start angle and the extent. The region of the oval can be filled or unfilled, and there are three different ways to define the fill region. The pieslice style connects the arc with the center point of the oval. The chord style connects the two end points of the arc. The arc style just draws the arc itself and there is no fill. Example 34-5 shows three arcs with the same bounding box but different styles and angles: Example 34-5 Canvas arc items.
pieslice -fill orange -outline black arc 10 10 100 100 -start 135 -extent 90 \ chord -fill blue -outline white -width 4 arc 10 10 100 100 -start 255 -extent 45 \ arc -outline black -width 3
The length of the arc in the counter-clockwise direction. The color of the interior of the arc region. The color of the arc itself. The stipple pattern for the outline of the arc. The starting angle of the arc. A stipple pattern for the fill.
pieslice, chord, arc.
List of tags for the arc item. Width, in canvas coordinates, of the arc and outline.
Bitmap Items
A bitmap is a simple graphic with a foreground and background color. One bit per pixel is used to choose between the foreground and the background. If you do not specify a background color, the background bits are clear and the canvas background shows through. A canvas bitmap item is positioned with two coordinates and an anchor position. Its size is determined by the bitmap data. The bitmap itself is specified with a symbolic name or by the name of a file that contains its definition. If the name begins with an @, it indicates a file name. The bitmaps built into wish are shown in the example below. Chapter 47 outlines the C interface for registering bitmaps under a name. Example 34-6 Canvas bitmap items.
set o [$c create bitmap 10 10 -bitmap @candle.xbm -anchor nw\ -background white -foreground blue] set x [lindex [$c bbox $o] 2] ;# Right edge of bitmap foreach builtin {error gray12 gray50 hourglass \ info questhead question warning} { incr x 20 set o [$c create bitmap $x 30 -bitmap $builtin -anchor c] set x [lindex [$c bbox $o] 2] } Table 34-2 specifies the complete set of bitmap attributes.
Anchor: c, n, ne, e, se, s, sw, w, or nw. The background color (for zero bits). A built-in bitmap. A bitmap defined by a file. The foreground color (for one bits). List of tags for the bitmap item.
Image Items
The canvas image objects use the general image mechanism of Tk. You must first define an image using the image command, which is described in Chapter 38 in the section Bitmaps and Images. Once you have defined an image, all you need to specify for the canvas is its position, anchor point, and any tags. The size and color information is set when the image is defined. If an image is redefined, anything displaying that image automatically gets updated. Example 34-7 creates one image and puts six instances of it on a canvas: Example 34-7 Canvas image items.
image create bitmap hourglass2 \ -file hourglass.bitmap -maskfile hourglass.mask \ -background white -foreground blue
for {set x 20} {$x < 300} {incr x 20} { $c create image $x 10 -image hourglass2 -anchor nw incr x [image width hourglass2] } Table 34-3 specifies the attributes for canvas image items.
Anchor: c, n, ne, e, se, s, sw, w, or nw. The name of an image. List of tags for the image item.
Line Items
A line has two or more sets of coordinates, where each set of coordinates defines an end point of a line segment. The segments can be joined in several different styles, and the whole line can be drawn with a spline fit as opposed to straight-line segments. The next example draws a line in two steps. In the first pass, single-segment lines are drawn. When the stroke completes, these are replaced with a single line segment that is drawn with a spline curve. Example 34-8 A canvas stroke drawing example.
proc StrokeInit {} { canvas .c ; pack .c bind .c <Button-1> {StrokeBegin %W %x %y} bind .c <B1-Motion> {Stroke %W %x %y} bind .c <ButtonRelease-1> {StrokeEnd %W %x %y} } proc StrokeBegin { w x y } { global stroke
catch {unset stroke} set stroke(N) 0 set stroke(0) [list $x $y] } proc Stroke { w x y } { global stroke set coords $stroke($stroke(N)) lappend coords $x $y incr stroke(N) set stroke($stroke(N)) [list $x $y] # eval gets the coordinates into individual arguments eval {$w create line}$coords {-tag segments} } proc StrokeEnd { w x y } { global stroke set coords {} for {set i 0} {$i <= $stroke(N)} {incr i} { append coords $stroke($i) " " } $w delete segments eval {$w create line}$coords \ {-tag line -joinstyle round -smooth true -arrow last} } Example 34-8 uses the stroke array to hold the points of the line as it builds up the stroke. At the end of the stroke it assembles the points into a list. The eval command concatenates this list of points onto the create line command. Recall that eval uses concat if it gets multiple arguments. The other parts of the create line command are protected by braces so they get evaluated only once. Chapter 10 describes this trick in more detail on page 126. The arrow attribute adds an arrow head to the end of the stroke. If you try this example you will notice that the arrow is not always aimed as you expect. This is because there are often many points generated close together as you release the mouse button. In fact, the X and Y coordinates seen by StrokeEnd are always the same as those seen by the last Stroke call. If you add this duplicate point to the end of the list of points, no arrowhead is drawn at all. In practice you might want to make Stroke filter out points that are too close together. Table 34-4 specifies the complete set of line attributes. The capstyle affects the way the ends of the line are drawn. The joinstyle affects the way line segments are joined together. The capstyle and joinstyle attributes are from the X window system and may not be implemented on the Macintosh and Windows platforms. Future versions of Tk may support dashed and dotted lines, too.
-arrow where -arrowshape {a b c} -capstyle what -fill color -joinstyle what -smooth boolean -splinesteps num -stipple bitmap -tags tagList -width width
Arrow location: none, first, last, or both. Three parameters that describe the shape of the arrow. c is the width and b is the overall length. a is the length of the part that touches the line (e.g., 8 10 3). Line ends: butt, projecting, or round. The color of the line. Line joints: bevel, miter, or round. If true, a spline curve is drawn. Number of line segments that approximate the spline. Stipple pattern for line fill. Set of tags for the line item. Width of the line, in screen units.
Oval Items
An oval is defined by two sets of coordinates that define its bounding box. If the box is square, a circle is drawn. You can set the color of the interior of the oval as well as the outline of the oval. A sampler of ovals is shown in Example 34-9. Example 34-9 Canvas oval items.
$c create oval 10 10 80 80 -fill red -width 4 $c create oval 100 10 150 80 -fill blue -width 0 $c create oval 170 10 250 40 -fill black -stipple gray12 Table 34-5 specifies the complete set of oval attributes.
-fill color -outline color -stipple bitmap -tags tagList -width width
The color of the interior of the oval. The color for the outline of the oval. Stipple pattern for oval fill. Set of tags for the oval item. The thickness of the outline.
Polygon Items
A polygon is a closed shape specified by sets of points, one for each vertex of the polygon. The vertices can be connected with smooth or straight lines. Example 34-10 creates a stop sign. The picture is centered at (0, 0) and then moved fully onto the canvas: Example 34-10 Canvas polygon items.
$c create poly 20 -40 40 -20 40 20 20 40 -20 40 \ -40 20 -40 -20 -20 -40 -fill red \ -outline white -width 5 $c create text 0 0 -text STOP -fill white \ -font {helvetica 18 bold} $c move all 50 50 Table 34-6 specifies the polygon attributes.
-fill color -outline color -smooth boolean -splinesteps num -stipple bitmap -tags tagList -width width
The color of the polygon. The color of the polygon's outline. If true, a spline curve is drawn around the points. Number of line segments that approximate the spline. Stipple pattern for polygon fill. Set of tags for the line item. The thickness of the outline.
Rectangle Items
A rectangle is specified with two coordinates that are its opposite corners. A rectangle can have a fill color and an outline color. If you do not specify a fill, then the background of the canvas (or other objects) shows through. If you stipple the fill, the background also shows through the clear bits of the stipple pattern. You must use a second rectangle if you want the stippled fill to completely hide what is behind it. Example 34-11 drags out a box as the user drags the mouse. All it requires is remembering the last rectangle drawn so that it can be deleted when the next box is drawn: Example 34-11 Dragging out a box. proc BoxInit {} { canvas .c -bg white ; pack .c bind .c <Button-1> {BoxBegin %W %x %y} bind .c <B1-Motion> {BoxDrag %W %x %y} } proc BoxBegin { w x y } { global box set box($w,anchor) [list $x $y] catch {unset box($w,last)} } proc BoxDrag { w x y } { global box catch {$w delete $box($w,last)} set box($w,last) [eval {$w create rect}$box($w,anchor) \ {$x $y -tag box}] } The example uses box($w,anchor) to record the start of the box. This is a list with the X and Y coordinates. The eval command is used so that this list can be spliced into the create rect command. Table 34-7 specifies the complete set of rectangle attributes:
-fill color -outline color -stipple bitmap -tags tagList -width width
The color of the interior of the rectangle. The color for the outline of the rectangle. Stipple pattern for rectangle fill. Set of tags for the rectangle item. The thickness of the outline.
Text Items
The canvas text item provides yet another way to display and edit text. It supports selection, editing, and it can extend onto multiple lines. The position of a text item is specified by one set of coordinates and an anchor position. The size of the text is determined by the number of lines and the length of each line. A new line is started if there is a newline in the text string. If a width is specified, in screen units, then any line that is longer than this is wrapped onto multiple lines. The wrap occurs before a space character. The editing and selection operations for text items use indices to specify positions within a given text item. These are very similar to those used in the entry widget. Table 34-8 summarizes the indices for canvas text items. There are several canvas operations that manipulate text items. These are similar to some of the operations of the entry widget. The dchars and select to operations treat the second index differently than the corresponding operations in the entry and text widget. The character at the second index is included in the operation (e.g., deleted), while in the entry and text widget it is not.
Index of the first character. Index just past the last character. Index a character, where number counts from zero. Index of the character right after the insertion cursor. Index of the first character in the selection. Index of the last character in the selection. Index of the character under the specified X and Y coordinate.
The canvas text operations are parameterized by the tag or ID of the canvas object being manipulated. If the tag refers to more than one object, then the operations apply to the first object in the display list that supports an insert cursor. The display list is described on page 496. Table 34-9 summarizes the operations on text items. In the table $t is a text item or tag and $c is a canvas.
Deletes the characters from first through last, or just the character at first. Sets input focus to the specified item, or returns the ID of the item with the focus if it is not given. Sets the insert cursor to just before index. Returns the numerical value of index. Inserts the string just before index. Moves the boundary of an existing selection. Clears the selection. Starts a selection. Returns the ID of the selected item, if any. Extends the selection to the specified index.
$c icursor $t index $c index $t index $c insert $t index string $c select adjust $t index $c select clear $c select from $t index $c select item $c select to $t index
There are no default bindings for canvas text items. Example 34-12 sets up some basic bindings for canvas text items. The <Button-1> and <Button-2> bindings are on the canvas as a whole. The rest of the bindings are on items with the text tag. You must add the text tag to text items that should share the editable text behavior. Small procedures are introduced for each binding to hide the details and any local variables needed in the operations. Canvas find overlapping vs. find closest.
The CanvasFocus procedure uses the canvas find overlapping operation to see if a text object has been clicked. This must be used because find closest finds an object no matter how far away it is. It also uses the type operation to make sure only text objects are given the focus. If you want other object types to respond to key events, you should change that. The CanvasPaste procedure does one of two things. It pastes the selection into the canvas item that has the focus. If no item has the focus, then a new text item is created with the selection as its value: Example 34-12 Simple edit bindings for canvas text items. proc Canvas_EditBind { c } { bind $c <Button-1> \ {CanvasFocus %W [%W canvasx %x] [%W canvasy %y]}
bind $c <Button-2> \ {CanvasPaste %W [%W canvasx %x] [%W canvasy %y]} bind $c <<Cut>> {CanvasTextCopy %W; CanvasDelete %W} bind $c <<Copy>> {CanvasTextCopy %W} bind $c <<Paste>> {CanvasPaste %W} $c bind text <Button-1> \ {CanvasTextHit %W [%W canvasx %x] [%W canvasy %y]} $c bind text <B1-Motion> \ {CanvasTextDrag %W [%W canvasx %x] [%W canvasy %y]} $c bind text <Delete> {CanvasDelete %W} $c bind text <Control-d> {CanvasDelChar %W} $c bind text <Control-h> {CanvasBackSpace %W} $c bind text <BackSpace> {CanvasBackSpace %W} $c bind text <Control-Delete> {CanvasErase %W} $c bind text <Return> {CanvasNewline %W} $c bind text <Any-Key> {CanvasInsert %W %A} $c bind text <Key-Right> {CanvasMoveRight %W} $c bind text <Control-f> {CanvasMoveRight %W} $c bind text <Key-Left> {CanvasMoveLeft %W} $c bind text <Control-b> {CanvasMoveLeft %W} } proc CanvasFocus {c x y} { focus $c set id [$c find overlapping [expr $x-2] [expr $y-2] \ [expr $x+2] [expr $y+2]] if {($id == {}) || ([$c type $id] != "text")} { set t [$c create text $x $y -text "" \ -tags text -anchor nw] $c focus $t $c select clear $c icursor $t 0 } } proc CanvasTextHit {c x y {select 1}} { $c focus current $c icursor current @$x,$y $c select clear $c select from current @$x,$y } proc CanvasTextDrag {c x y} { $c select to current @$x,$y } proc CanvasDelete {c} { if {[$c select item] != {}} { $c dchars [$c select item] sel.first sel.last } elseif {[$c focus] != {}} { $c dchars [$c focus] insert } } proc CanvasTextCopy {c} { if {[$c select item] != {}} { clipboard clear
set t [$c select item] set text [$c itemcget $t -text] set start [$c index $t sel.first] set end [$c index $t sel.last] clipboard append [string range $text $start $end] } elseif {[$c focus] != {}} { clipboard clear set t [$c focus] set text [$c itemcget $t -text] clipboard append $text } } proc CanvasDelChar {c} { if {[$c focus] != {}} { $c dchars [$c focus] insert } } proc CanvasBackSpace {c} { if {[$c select item] != {}} { $c dchars [$c select item] sel.first sel.last } elseif {[$c focus] != {}} { set _t [$c focus] $c icursor $_t [expr [$c index $_t insert]-1] $c dchars $_t insert } } proc CanvasErase {c} { $c delete [$c focus] } proc CanvasNewline {c} { $c insert [$c focus] insert \n } proc CanvasInsert {c char} { $c insert [$c focus] insert $char } proc CanvasPaste {c {x {}} {y {}}} { if {[catch {selection get}_s] && [catch {selection get -selection CLIPBOARD}_s]} { return ;# No selection } set id [$c focus] if {[string length $id] == 0 } { set id [$c find withtag current] } if {[string length $id] == 0 } { # No object under the mouse if {[string length $x] == 0} { # Keyboard paste set x [expr [winfo pointerx $c] - [winfo rootx $c]] set y [expr [winfo pointery $c] - [winfo rooty $c]] } CanvasFocus $c $x $y
} else { $c focus $id } $c insert [$c focus] insert $_s } proc CanvasMoveRight {c} { $c icursor [$c focus] [expr [$c index current insert]+1] } proc CanvasMoveLeft {c} { $c icursor [$c focus] [expr [$c index current insert]-1] } Table 34-10 specifies the complete set of attributes for text items. Note that there are no foreground and background attributes. Instead, the fill color specifies the color for the text. It is possible to stipple the text as well.
Anchor: c, n, ne, e, se, s, sw, w, or nw. The foreground color for the text. The font for the text. Justification: left, right, or center. Stipple pattern for the text fill. Set of tags for the rectangle item. The string to display. The width, in screen units, before text is wrapped
Window Items
A window item lets you position other Tk widgets on a canvas. The position is specified by one set of coordinates and an anchor position. You can also specify the width and height, or you can let the widget determine its own size. The following example uses a canvas to provide a scrolling surface for a large set of labeled entries. A frame is created and a set of labeled entry widgets are packed into it. This main frame is put onto the canvas as a single window item. This way we let grid take care of arranging all the labeled entries. The size of the canvas is set up so that a whole number of labeled entries are displayed. The scroll region and scroll increment are set up so that clicking on the scrollbar arrows brings one new labeled entry completely into view. Example 34-13 Using a canvas to scroll a set of widgets.
proc Example34?3 {top title labels } { # Create a resizable toplevel window toplevel $top wm minsize $top 200 100 wm title $top $title # Create a frame for buttons, # Only Dismiss does anything useful set f [frame $top.buttons -bd 4] button $f.quit -text Dismiss -command "destroy $top" button $f.save -text Save button $f.reset -text Reset pack $f.quit $f.save $f.reset -side right pack $f -side top -fill x # Create a scrolling canvas frame $top.c canvas $top.c.canvas -width 10 -height 10 \ -yscrollcommand [list $top.c.yscroll set] scrollbar $top.c.yscroll -orient vertical \ -command [list $top.c.canvas yview] pack $top.c.yscroll -side right -fill y pack $top.c.canvas -side left -fill both -expand true pack $top.c -side top -fill both -expand true Scrolled_EntrySet $top.c.canvas $labels } proc Scrolled_EntrySet { canvas labels } { # Create one frame to hold everything # and position it on the canvas set f [frame $canvas.f -bd 0] $canvas create window 0 0 -anchor nw -window $f # Create and grid the labeled entries set i 0 foreach label $labels { label $f.label$i -text $label entry $f.entry$i grid $f.label$i $f.entry$i grid $f.label$i -sticky w grid $f.entry$i -sticky we incr i } set child $f.entry0 # Wait for the window to become visible and then # set up the scroll region based on # the requested size of the frame, and set # the scroll increment based on the # requested height of the widgets
tkwait visibility $child set bbox [grid bbox $f 0 0] set incr [lindex $bbox 3] set width [winfo reqwidth $f] set height [winfo reqheight $f] $canvas config -scrollregion "0 0 $width $height" $canvas config -yscrollincrement $incr set max [llength $labels] if {$max > 10} { set max 10 } set height [expr $incr * $max] $canvas config -width $width -height $height } Example34?3 .ex "An example" { alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega} The tkwait visibility command is important to the example. It causes the script to suspend execution until the top-level window, $top, is displayed on the screen. The tkwait is necessary so the right information gets returned by the grid bbox commands. By waiting for a subframe of the main frame, $child, we ensure that grid has gone through all its processing to position the interior widgets. The canvas's scroll region is set to be just large enough to hold the complete frame. The scroll increment is set to the height of one of the grid cells. Each click on the scrollbar arrows brings one new grid row completely into view.
Top
Table of Contents
Canvas Operations
Table 34-11 summarizes the operations on canvas widgets. In the table, $c is a canvas and $t represents a canvas tag or numerical object ID. In some cases, an operation only applies to a single object. In these cases, if a tag identifies several objects, the first object in the display list is operated on. The canvas display list refers to the global order among canvas objects. New objects are put at the end of the display list. Objects later in the display list obscure objects earlier in the list. The term above refers to objects later in the display list. Table 34-9 describes several of the canvas operations that only apply to text objects. They are dchars, focus, index, icursor , insert, and select. Table 34-11 does not repeat those operations.
Adds tag to the item just above $t in the display list. Adds tag to all objects in the canvas. Adds tag to the item just below $t in the display list. Adds tag to the item closest to the x y position. If more than one object is the same distance away, or if more than one object is within halo pixels, then the last one in the display list (uppermost) is returned. If start is specified, the closest object after start in the display list is returned. <=
$c addtag tag Adds tag to the items completely enclosed in the specified region. x1 enclosed x1 y1 x2 y2 x2 , y1 <= y2 . $c addtag tag withtag $t
$c bbox $t ?tag tag ...? $c bind $t ? sequence? ?command? $c canvasx screenx ? grid? $c canvasy screeny ? grid? $c cget option $c configure ... $c coords $t ?x1 y1 ...? $c create type x y ? x2 y2 ...? ?opt value ...? $c delete $t ?tag ...? $c dtag $t ?deltag?
Returns the bounding box of the items identified by the tag(s) in the form
x1 y1 x2 y2
Sets or queries the bindings of canvas items. Maps from the X screen coordinate screenx to the X coordinate in canvas space, rounded to multiples of grid if specified. Maps from screen Y to canvas Y. Returns the value of option for the canvas. Queries or updates the attributes of the canvas. Queries or modifies the coordinates of the item. Creates a canvas object of the specified type at the specified coordinates.
Deletes the item(s) specified by the tag(s) or ID(s). Removes the specified tags from the items identified by $t. If deltag is omitted, it defaults to $t.
$c find addtagSearch Returns the IDs of the tags that match the search specification: above, all, ... below, closest , enclosed, and withtag , as for addtag. $c gettags $t $c itemcget $t option $c itemconfigure $t ... $c lower $t ? belowThis? $c move $t dx dy $c postscript ... $c raise $t ? aboveThis?
Returns the tags associated with the first item identified by $t. Returns the value of option for item $t. Queries or reconfigures item $t. Moves the items identified by $t to the beginning of the display list, or just before belowThis. Moves $t by the specified amount. Generates postscript. Table 34-12 lists options. Moves the items identified by $t to the end of the display list, or just after aboveThis.
$c scale $t x0 y0 xS Scales the coordinates of the items identified by $t . The distance between yS x0 and a given X coordinate changes by a factor of xS . Similarly for Y. $c scan mark x y $c scan dragto x y $c type $t
Sets a mark for a scrolling operation. Scrolls the canvas from the previous mark. Returns the type of the first item identified by $t.
$t xview $t xview moveto fraction $t xview scroll num what $t yview $t yview moveto fraction $t yview scroll num what
Returns two fractions between zero and one that describes the amount of the canvas off-screen to the left and the amount of the canvas displayed. Positions the canvas so that fraction of the scroll region is off screen to the left. Scrolls num of what, which is units or pages. Returns two fractions between zero and one that describes the amount of the canvas off screen to the top and the amount of the canvas displayed. Positions the text so that fraction of the canvas scroll region is off screen toward the top. Scrolls num of what, which is units or pages.
Top
Table of Contents
Generating Postscript
The postscript operation generates postscript based on the contents of a canvas. One limitation to note is that embedded windows are not captured in the postscript output. Table 34-12 summarizes all the options for generating postscript:
The index of varName is a named color, and the contents of each element is the postscript code to generate the RGB values for that color. one of color, grey, or mono. The file in which to write the postscript. If not specified, the postscript is returned as the result of the command. The index of varName is an X font name. Each element contains a list of two items: a postscript font name and a point size. Height of the area to print. Anchor: c, n, ne, e, se, s, sw, w, or nw. Height of image on the output. A floating point number followed by c (centimeters), i (inches), m (millimeters), or p (printer points). Width of image on the output. The output X coordinate of the anchor point. The output Y coordinate of the anchor point. If true, rotates so that X axis is the long direction of the page (landscape orientation).
-colormode mode mode is -file name -fontmap varName -height size -pageanchor anchor -pageheight size -pagewidth size -pagex position -pagey position -rotate boolean
Width of the area to print. Canvas X coordinate of left edge of the image. Canvas Y coordinate of top edge of the image.
You control what region of the canvas is printed with the -width, -height, -x, and -y options. You control the size and location of this in the output with the -pageanchor, -pagex, -pagey, -pagewidth, and -pageheight options. The postscript is written to the file named by the -file option, or it is returned as the value of the postscript canvas operation. You control fonts with a mapping from X screen fonts to postscript fonts. Define an array where the index is the name of the X font and the contents are the name and pointsize of a postscript font. The next example positions a number of text objects with different fonts onto a canvas. For each different X font used, it records a mapping to a postscript font. The example has a fairly simple font mapping, and in fact the canvas would probably have guessed the same font mapping itself. If you use more exotic screen fonts, you may need to help the canvas widget with an explicit font map. The example positions the output at the upper-left corner of the printed page by using the -pagex, pagey, and -pageanchor options. Recall that postscript has its origin at the lower-left corner of the page. Example 34-14 Generating postscript from a canvas. proc Setup {} { global fontMap canvas .c pack .c -fill both -expand true set x 10 set y 10 set last [.c create text $x $y -text "Font sampler" \ -font fixed -anchor nw] # Create several strings in different fonts and sizes foreach family {times courier helvetica} { set weight bold switch -- $family { times {set fill blue; set psfont Times} courier {set fill green; set psfont Courier } helvetica {set fill red; set psfont Helvetica } } foreach size {10 14 24} { set y [expr 4+[lindex [.c bbox $last] 3]] # Guard against missing fonts if {[catch {.c create text $x $y \ -text $family-$weight-$size \ -anchor nw -fill $fill \ -font -*-$family-$weight-*-*-*-$size-*}\
it] == 0} { set fontMap(-*-$family-$weight-*-*-*-$size-*)\ [list $psfont $size] set last $it } } } set fontMap(fixed) [list Courier 12] } proc Postscript { c file } { global fontMap # Tweak the output color set colorMap(blue) {0.1 0.1 0.9 setrgbcolor} set colorMap(green) {0.0 0.9 0.1 setrgbcolor} # Position the text at the upper-left corner of # an 8.5 by 11 inch sheet of paper $c postscript -fontmap fontMap -colormap colorMap \ -file $file \ -pagex 0.i -pagey 11.i -pageanchor nw }
Top
Table of Contents
Canvas Attributes
Table 34-13 lists the attributes for the canvas widget. The table uses the resource name, which has capitals at internal word boundaries. In Tcl commands, the attributes are specified with a dash and are all lowercase.
The normal background color. The width of the border around the canvas. Distance from mouse to an overlapping object. Boolean. True constrains the view to the scroll region. Cursor to display when mouse is over the widget. Height, in screen units, of canvas display. Focus highlight color when widget does not have focus. Color for input focus highlight border. Width of highlight border. Background for area covered by insert cursor. Width of cursor border. Nonzero for 3D effect. Time, in milliseconds the insert cursor blinks off. Time, in milliseconds the insert cursor blinks on. Width of insert cursor. Default is 2.
flat, sunken, raised, groove, solid,
or ridge.
scrollRegion selectBackground selectForeground selectBorderWidth takeFocus width xScrollCommand xScrollIncrement yScrollCommand yScrollIncrement
Left, top, right, and bottom coordinates of the canvas. Background color of selection. Foreground color of selection. Width of selection border. Nonzero for 3D effect. Controls focus changes from keyboard traversal. Width in screen units for viewable area. Tcl command prefix for horizontal scrolling. Distance for one scrolling unit in the X direction. Tcl command prefix for vertical scrolling. Distance for one scrolling unit in the Y direction.
The scroll region of a canvas defines the boundaries of the canvas coordinate space. It is specified as four coordinates, x1 y1 x2 y2 where (x1, y1) is the top-left corner and (x2, y2) is the lower-right corner. If the confine attribute is true, then the canvas cannot be scrolled outside this region. It is OK to position canvas objects partially or totally off the scroll region; they just may not be visible. The scroll increment attributes determine how much the canvas is scrolled when the user clicks on the arrows in the scrollbar. The closeEnough attribute indicates how far away a position can be from an object and still be considered to overlap it. This applies to the overlapping search criteria.
Top
Table of Contents
Hints
Resources
There is no resource database support built into the canvas and its items. You can, however, define resources and query them yourself. For example, you could define: *Canvas.foreground: blue
This would have no effect by default. However, your code could look for this resource with option get, and specify this color directly for the -fill attribute of your objects: set fg [option get $c foreground {}] $c create rect 0 0 10 10 -fill $fg The main reason to take this approach is to let your users customize the appearance of canvas objects without changing your code.
Top
Table of Contents
Part V: Tk Details
Part V describes the rest of the Tk toolkit. Chapter 35 describes the selection mechanism that is used for cut and paste between applications. It includes an example that implements cut and paste of graphical objects on a canvas. Chapter 36 describes dialogs. Tk has several built-in dialogs that use the native platform look and feel. The chapter also describes how to build your own dialogs. Chapter 37 is the first of three chapters that explain widget attributes in more detail. It describes size and layout attributes. Chapter 38 describes colors, images, and cursors. It explains how to use the bitmap and color photo image types. The chapter includes a complete map of the cursor font. Chapter 39 describes fonts and other text-related attributes. The extended example is a font selection application. Chapter 40 describes the Tk send command that lets you send commands among Tk applications. It also presents a socket-based alternative that can be used among applications on different hosts and with the Safe-Tcl mechanism to limit the power of remotely invoked commands. Chapter 41 explains how to interact with the window manager using the wm command. The chapter describes all the information available through the winfo command. Chapter 42 builds upon Chapter 28 to create a user preferences package and an associated user interface. The preference package links a Tcl variable used in your application to a resource specification. Chapter 43 presents a user interface to the binding mechanism. You can browse and edit bindings for widgets and classes with the interface.
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
If you want a mechanism that works on all platforms, use the CLIPBOARD selection. The PRIMARY selection is implemented by Tk on all platforms, and you can use it within an application, but on
Windows and Macintosh the non-Tk applications do not know about the PRIMARY selection. The main goal of copy and paste is to provide general interoperability among all applications, so stick with the CLIPBOARD. Tk 3.6 and earlier only supported the PRIMARY selection. When Tk 4.0 added support for the CLIPBOARD, I tried to merge the two selections to "simplify" things for my users. Example 35-1 implements a Paste function that inserts either the PRIMARY or CLIPBOARD selection into a text widget. The selection get command is used to retrieve the selection value: Example 35-1 Paste the PRIMARY or CLIPBOARD selection. proc Paste { text } { if [catch {selection get}sel] { if [catch {selection get -selection CLIPBOARD}sel] { # no selection or clipboard data return } } $text insert insert $sel } This Paste function can be convenient, but it turns out that users still need to keep track of the difference between the two selections. If a user only understands the CLIPBOARD, then the use of PRIMARY is only surprising. I learned that it is best to have a separate paste user action for the two selections. The convention is that <ButtonRelease-2> sets the insert point and inserts the PRIMARY selection. (This convention is awkward with the one- and two-button mice on Macintosh and Windows.) The <<Paste>> event (e.g., the Paste key) simply inserts the CLIPBOARD selection at the current insert point. This convention is shown in Example 35-2, although these bindings are defined automatically for the text and entry widgets: Example 35-2 Separate paste actions. bind Text <<Paste>> { catch {%W insert insert \ [selection get -selection CLIPBOARD] } } bind Text <ButtonRelease-2> { %W mark set insert @%x,%y catch {%W insert insert \ [selection get -selection PRIMARY] } }
Top
Table of Contents
selection clear ?-displayof win? ?selection sel? selection get ?displayof win? ?selection sel? ?-type type? selection handle ?-selection sel? ? -type type? ?-format format? window command selection own ?-displayof window? ? -selection sel? selection own ?-command command? ?selection sel? window
Clears the specified selection. Returns the specified selection. The type defaults to STRING. Defines command to be the handler for selection requests when window owns the selection. Returns the Tk pathname of the window that owns the selection, if it is in this application. Asserts that window owns the sel selection. The command is called when ownership of the selection is taken away from window.
Top
Table of Contents
Clears the CLIPBOARD selection. Appends data to the CLIPBOARD with the specified type and format, which both default to STRING.
Top
Table of Contents
Selection Handlers
The selection handle command registers a Tcl command to handle selection requests. The command is called to return the value of the selection to a requesting application. If the selection value is large, the command might be called several times to return the selection in pieces. The command gets two parameters that indicate the offset within the selection to start returning data, and the maximum number of bytes to return. If the command returns fewer than that many bytes, the selection request is assumed to be completed. Otherwise, the command is called again to get the rest of the data, and the offset parameter is adjusted accordingly. You can also get a callback when you lose ownership of the selection. At that time it is appropriate to unhighlight the selected object in your interface. The selection own command sets ownership and registers a callback for when you lose ownership.
bind $c <Button-2> [list CanvasPaste $c %x %y] # Register the handler for selection requests selection handle $c [list CanvasSelectHandle $c] } The CanvasSelect procedure selects an object. It uses the find closest canvas operation to find out what object is under the mouse, which works because the binding is on canvas items with the object tag. If the binding were on the canvas as a whole, you would use the find overlapping operation to limit selection to objects near the mouse click. The CanvasHighlight procedure is used to highlight the selected object. It displays small boxes at the corners of the object's bounding box. Finally, the CanvasSelectLose procedure is registered to be called when another application asserts ownership of the PRIMARY selection. Example 35-4 Selecting objects. proc CanvasSelect { w x y } { # Select an item on the canvas. global canvas set id [$w find closest $x $y] set canvas(select,$w) $id CanvasHighlight $w $id # Claim ownership of the PRIMARY selection selection own -command [list CanvasSelectLose $w] $w focus $w } proc CanvasHighlight {w id {clear clear}} { if {$clear == "clear"} { $w delete highlight } foreach {x1 y1 x2 y2}[$w bbox $id] {# lassign } foreach x [list $x1 $x2] { foreach y [list $y1 $y2] { $w create rectangle [expr $x-2] [expr $y-2] \ [expr $x+2] [expr $y+2] -fill black \ -tag highlight } } } proc CanvasSelectLose { w } { # Some other app has claimed the selection global canvas $w delete highlight unset canvas(select,$w) } Once you claim ownership, Tk calls back to the CanvasSelectHandle procedure when another application, even yours, requests the selection. This uses CanvasDescription to compute a description of the canvas object. It uses canvas operations to query the object's configuration and store
that as a command that will create the object: Example 35-5 A canvas selection handler. proc CanvasSelectHandle {w offset maxbytes } { # Handle a selection request global canvas if ![info exists canvas(select,$w)] { error "No selected item" } set id $canvas(select,$w) # Return the requested chunk of data. return [string range [CanvasDescription $w $id] \ $offset [expr $offset+$maxbytes]] } proc CanvasDescription { w id } { # Generate a description of the object that can # be used to recreate it later. set type [$w type $id] set coords [$w coords $id] set config {} # Bundle up non-default configuration settings foreach conf [$w itemconfigure $id] { # itemconfigure returns a list like # -fill {} {} {}red set default [lindex $conf 3] set value [lindex $conf 4] if {[string compare $default $value] != 0} { lappend config [lindex $conf 0] $value } } return [concat CanvasObject $type $coords $config] } The CanvasCopy procedure puts the description of the selected item onto the clipboard with the clipboard append command. The CanvasDelete deletes an object and the highlighting, and CanvasCut is built from CanvasCopy and CanvasDelete : Example 35-6 The copy and cut operations. proc CanvasCopy { w } { global canvas if [info exists canvas(select,$w)] { set id $canvas(select,$w) clipboard clear clipboard append [CanvasDescription $w $id] } }
proc CanvasDelete {w} { global canvas catch { $w delete highlight $w delete $canvas(select,$w) unset canvas(select,$w) } } proc CanvasCut { w } { CanvasCopy $w CanvasDelete $w } The CanvasPaste operation gets the value from the CLIPBOARD selection. The selection value has all the parameters needed for a canvas create operation. It gets the position of the new object from the <Button-2> event, or from the current mouse position if the <<Paste>> event is generated. If the mouse is out of the window, then the object is just put into the middle of the canvas. The original position and the new position are used to compute values for a canvas move: Example 35-7 Pasting onto the canvas. proc CanvasPaste {w {x {}} {y {} }} { # Paste the selection from the CLIPBOARD if [catch {selection get -selection CLIPBOARD}sel] { # no clipboard data return } if {[string length $x] == 0} { # <<Paste>>, get the current mouse coordinates set x [expr [winfo pointerx $w] - [winfo rootx $w]] set y [expr [winfo pointery $w] - [winfo rooty $w]] if {$x < 0 || $y < 0 || $x > [winfo width $w] || $y > [winfo height $w]} { # Mouse outside the window - center object set x [expr [winfo width $w]/2] set y [expr [winfo height $w]/2] } } if [regexp {^CanvasObject}$sel] { if [catch {eval {$w create}[lrange $sel 1 end]}id] { return; } # look at the first coordinate to see where to # move the object. Element 1 is the type, the # next two are the first coordinate set x1 [lindex $sel 2] set y1 [lindex $sel 3] $w move $id [expr $x-$x1] [expr $y-$y1]
} } There is more you can do for a drawing program, of course. You'd like to be able to select multiple objects, create new ones, and more. The canvas_ui program on the CD-ROM was my first little effort at a canvas drawing program. The ImPress application by Christopher Cox is a full-featured page layout application based on the Tk canvas. You can find it on the CD-ROM and on the Web at: http://www.ntlug.org/~ccox/impress/
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
Standard Dialogs
The tk_dialog command presents a choice of buttons and returns a number indicating which one was clicked by the user. The general form of the command is: tk_dialog win title text bitmap default ?label? ?label? ... The title appears in the title bar, and the text appears in the dialog. The bitmap appears to the left of the text. Specify {} for the bitmap if you do not want one. The set of built-in bitmaps is given on page 541. The label arguments give labels that appear on buttons along the bottom of the dialog. The default argument gives the index of the default button, counting from zero. If there is no default, specify {} or -1.
Message Box
The tk_messageBox dialog is a limited form of tk_dialog that has native implementations on the different platforms. Like tk_dialog, it allows for a message, bitmap, and a set of buttons. However, the button sets are predefined, and the bitmaps are limited. The yesno button set, for example, displays a Yes and a No button. The abortretryignore button set displays Abort, Retry, and Ignore buttons. The tk_messageBox command returns the symbolic name of the selected button (e.g., yes or retry.) The yesnocancel message box could be used when trying to quit with unsaved changes: set choice [tk_messageBox -type yesnocancel -default yes \ -message "Save changes before quitting?" \ -icon question] The complete set of options to tk_messageBox is listed in Table 36-1:
-default name -icon name -message string -parent window -title title -type type
Default button name (e.g., yes) Name: error, info, question, or warning. Message to display. Embeds dialog in window. Dialog title (UNIX and Windows) Type: abortretrycancel, ok, okcancel, retrycancel, yesno, or yesnocancel
File Dialogs
There are two standard file dialogs, tk_getOpenFile and tk_getSaveFile. The tk_getOpenFile dialog is used to find an existing file, while tk_getSaveFile can be used to find a new file. These procedures return the selected file name, or the empty string if the user cancels the operation. These procedures take several options that are listed in Table 36-2:
a set of file types that the user can select to limit the files displayed in the dialog. Lists contents of dir in the initial display. Default file, for tk_getSaveFile only. Creates the dialog as an embedded child of window. Displays string in the title (UNIX and Windows).
The file dialogs can include a listbox that lists different file types. The file types are used to limit the directory listing to match only those types. The typelist option specifies a set of file extensions and Macintosh file types that correspond to a named file type. If you do not specify a typelist, users just see all the files in a directory. Each item in typelist is itself a list of three values: name extensions ?mactypes? The name is displayed in the list of file types. The extensions is a list of file extensions corresponding to that type. The empty extension "" matches files without an extension, and the extension * matches all files. The mactypes is an optional list of four-character Macintosh file types, which are ignored on other platforms. On the Macintosh, if you give both extensions and mactypes, the files must match both. If the extensions is an empty list, only the mactypes are considered. However, you can repeat name in the typelist and give extensions in one set and mactypes in another set. If you do this, then files that match either the extensions or mactypes are listed.
The following typelist matches Framemaker Interchange Files that have both a .mif extension and a MIF type: set typelist { {"Maker Interchange Files" {".mif"} {"MIF "}} } The following typelist matches GIF image files that have either a .gif extension or the GIFF file type. Note that the mactypes are optional: set typelist { {"GIF Image" {".gif"}} {"GIF Image" {} {"GIFF"}}} } The following typelist puts all these together, along with an entry for all files. The entry that comes first is displayed first: set typelist { {"All Files" {*}} {"GIF Image" {".gif"}} {"GIF Image" {} {"GIFF"}} {"Maker Interchange Files" {".mif"} {"MIF "}} }
Color Dialog
The tk_chooseColor dialog displays a color selection dialog. It returns a color, or the empty string if the user cancels the operation. The options to tk_chooseColor are listed in Table 36-3:
Initial color to display. Creates the dialog as an embedded child of window. Displays string in the title (UNIX and Windows).
Top
Table of Contents
Custom Dialogs
When you create your own dialogs, you need to understand keyboard focus, focus grabs, and how to wait for the user to finish with a dialog. Here is the general structure of your code when creating a dialog: # Create widgets, then focus $toplevel grab $toplevel tkwait window $toplevel This sequence of commands directs keyboard focus to the toplevel containing your dialog. The grab forces the user to interact with the dialog before using other windows in your application. The tkwait command returns when the toplevel window is destroyed, and this automatically releases the grab. This assumes that the button commands in the dialog destroy the toplevel. The following sections explain these steps in more detail, and Example 36-1 on page 519 illustrates a more robust sequence.
Input Focus
The window system directs keyboard events to the toplevel window that currently has the input focus. The application, in turn, directs the keyboard events to one of the widgets within that toplevel window. The focus command sets focus to a particular widget, and it is used by the default bindings for Tk widgets. Tk remembers what widget has focus within a toplevel window and automatically gives focus to that widget when the system gives focus to a toplevel window. On Windows and Macintosh, the focus is given to an application when you click in its window. On UNIX, the window manager application gives focus to different windows, and window managers allow different conventions to shift focus. The click-to-type model is similar to Windows and Macintosh. There is also focus-follows-mouse, which gives focus to the window under the mouse. One thing to note about click-to-type is that the application does not see the mouse click that gives the window focus. Once the application has focus, you can manage the focus changes among your widgets any way you like. By default, Tk uses a click-to-type model. Text and entry widgets set focus to themselves when
you click on them with the left mouse button. You can get the focus-follows-mouse model within your widgets by calling the tk_focusFollowsMouse procedure. However, in many cases you will find that an explicit focus model is actually more convenient for users. Carefully positioning the mouse over a small widget can be tedious.
Returns the widget that currently has the focus on the display of the application's main window. Sets the focus to window. The -force option ignores the window manger, so use it sparingly. Returns the focus widget on the same display as win. Returns the name of the last widget to have the focus in the same toplevel as win.
0 1
indicates the widget should not take focus. indicates the widget should always take focus.
An empty string means the traversal procedures tk_focusNext and tk_focusPrev should decide based on the widget's state and bindings. Otherwise the value is a Tcl command prefix. The command is called with the widget name as an argument, and it should return either 0, 1, or the empty string.
Sets a grab to a particular window. Queries the grabs on the display of window, or on all displays if window is omitted. Releases a grab on window. Sets a grab to a particular window. Returns none, local, or global.
In most cases you only need to use the grab and grab release commands. Note that the grab set command is equivalent to the grab command. The next section includes examples that use the grab command.
Waits for the global variable varname to be set. This is just like the vwait command. Waits for the window win to become visible. Waits for the window win to be destroyed.
The variable specified in the tkwait variable command must be a global variable. Remember this if you use procedures to modify the variable. They must declare it global or the tkwait command will not notice the assignments. The tkwait visibility waits for the visibility state of the window to change. Most commonly this is used to wait for a newly created window to become visible. For example, if you have any sort of animation in a complex dialog, you could wait until the dialog is displayed before starting the animation.
Destroying Widgets
The destroy command deletes one or more widgets. If the widget has children, all the children are destroyed, too. Chapter 41 describes a protocol on page 572 to handle destroy events that come from the window manager. You wait for a window to be deleted with the tkwait window command.
This sequence supports another trick I use, which is to unmap dialogs instead of destroying them. This way the dialogs appear more quickly the next time they are used. This makes creating the dialogs a little more complex because you need to see if the toplevel already exists. Chapter 41 describes the window manager commands used to map and unmap windows on page 572. Example 36-1 shows Dialog_Create, Dialog_Wait, and Dialog_Dismiss that capture all of these tricks: Example 36-1 Procedures to help build dialogs. proc Dialog_Create {top title args} { global dialog if [winfo exists $top] { switch -- [wm state $top] { normal { # Raise a buried window raise $top } withdrawn iconic { # Open and restore geometry wm deiconify $top catch {wm geometry $top $dialog(geo,$top)} } } return 0 } else { eval {toplevel $top}$args wm title $top $title return 1 } } proc Dialog_Wait {top varName {focus {}}} { upvar $varName var # Poke the variable if the user nukes the window bind $top <Destroy> [list set $varName cancel] # Grab focus for the dialog if {[string length $focus] == 0} { set focus $top } set old [focus -displayof $top] focus $focus catch {tkwait visibility $top} catch {grab $top} # Wait for the dialog to complete tkwait variable $varName catch {grab release $top} focus $old }
proc Dialog_Dismiss {top} { global dialog # Save current size and position catch { # window may have been deleted set dialog(geo,$top) [wm geometry $top] wm withdraw $top } } The Dialog_Wait procedure allows a different focus widget than the toplevel. The idea is that you can start the focus out in the appropriate widget within the dialog, such as the first entry widget. Otherwise, the user has to click in the dialog first. Grab can fail.
The catch statements in Dialog_Wait come from my experiences on different platforms. The tkwait visibility is sometimes required because grab can fail if the dialog is not yet visible. However, on other systems, the tkwait visibility itself can fail in some circumstances. Tk reflects these errors, but in this case all that can go wrong is no grab. The user can still interact with the dialog without a grab, so I just ignore these errors.
Prompter Dialog
Example 36-2 A simple dialog.
proc Dialog_Prompt { string } { global prompt set f .prompt if [Dialog_Create $f "Prompt" -borderwidth 10] { message $f.msg -text $string -aspect 1000
entry $f.entry -textvariable prompt(result) set b [frame $f.buttons] pack $f.msg $f.entry $f.buttons -side top -fill x pack $f.entry -pady 5 button $b.ok -text OK -command {set prompt(ok) 1} button $b.cancel -text Cancel \ -command {set prompt(ok) 0} pack $b.ok -side left pack $b.cancel -side right bind $f.entry <Return> {set prompt(ok) 1 ; break} bind $f.entry <Control-c> {set prompt(ok) 0 ; break} } set prompt(ok) 0 Dialog_Wait $f prompt(ok) $f.entry Dialog_Dismiss $f if {$prompt(ok)} { return $prompt(result) } else { return {} } } Dialog_Prompt "Please enter a name" The Dialog_Prompt dialog gets a value from the user, returning the value entered, or the empty string if the user cancels the operation. Dialog_Prompt uses the Tcl variable prompt(ok) to indicate the dialog is complete. The variable is set if the user presses the OK or Cancel buttons, or if the user presses <Return> or <Control-c> in the entry widget. The Dialog_Wait procedure waits on prompt(ok), and it grabs and restores focus. If the Dialog_Create procedure returns 1, then the dialog is built: otherwise, it already existed.
Top
Table of Contents
The safest way to use update is with its idletasks option. If you use the update command with no options, then all events are processed. In particular, user input events are processed. If you are not careful, it can have unexpected effects because another thread of execution is launched into your Tcl interpreter. The current thread is suspended and any callbacks that result from input events are executed. It is usually better to use the tkwait command if you need to process input because it pauses the main application at a well-defined point. One drawback of update idletasks is that in some cases a widget's redisplay is triggered by window system events. In particular, when you change the text of a label, it can cause the size of the label to change. The widget is too clever for us in this case. Instead of scheduling a redisplay at idle time, it requests a different size and then waits for the <Configure> event from the window system. The <Configure> event indicates a size has been chosen by the geometry manager, and it is at that point that the label schedules its redisplay. So, changing the label's text and doing update idletasks do not work as expected.
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
Configuring Attributes
You specify attributes for Tk widgets when you create them. You can also change them dynamically at any time after that. In both cases the syntax uses pairs of arguments. The first item in the pair identifies the attribute, the second provides the value. For example, a button can be created like this: button .doit -text Doit -command DoSomething The name of the button is .doit, and two attributes are specified: the text and the command. You can change the .doit button later with the configure widget operation: .doit configure -text Stop -command StopIt The current configuration of a widget can be queried with another form of the configure operation. If you just supply an attribute, the settings associated with that attribute are returned: .doit configure -text => -text text Text {} Stop This command returns several pieces of information: the command line switch, the resource name, the resource class, the default value, and the current value. If you don't give any options to configure, then the configuration information for all the attributes is returned. The following loop formats the information: foreach item [$w configure] { puts "[lindex $item 0] [lindex $item 4]" } If you just want the current value, use the cget operation:
.doit cget -text => Stop You can also configure widget attributes indirectly by using the resource database. An advantage of using the resource database is that users can reconfigure your application without touching the code. Otherwise, if you specify attribute values explicitly in the code, they cannot be overridden by resource settings. This is especially important for attributes like fonts and colors. The tables in this chapter list the attributes by their resource name, which may have a capital letter at an internal word boundary (e.g., activeBackground). When you specify attributes in a Tcl command, use all lowercase instead, plus a leading dash. Compare: option add *Button.activeBackground red $button configure -activebackground red The first command defines a resource that affects all buttons created after that point, and the second command changes an existing button. Command-line settings override resource database specifications. Chapter 28 describes the use of resources in detail.
Top
Table of Contents
Size
Most widgets have a width and height attribute that specifies their desired size, although there are some special cases. In all cases, the geometry manager for a widget might modify the size to some degree. The winfo operations described on page 573 return the current size of a widget. Most of the text-related widgets interpret their sizes in units of characters for width and lines for height. All other widgets, including the message widget, interpret their dimensions in screen units, which are pixels by default. The tk scale command, which is described on page 582, controls the scale between pixels and the other measures. You can suffix the dimension with a unit specifier to get a particular measurement unit: c i m p centimeters inch millimeters printer points (1/72 inches)
Scales and scrollbars can have two orientations as specified by the orient attribute, so width and height are somewhat ambiguous. These widgets do not support a height attribute, and they interpret their width attribute to mean the size of their narrow dimension. The scale has a length attribute that determines its long dimension. Scrollbars do not even have a length. Instead, a scrollbar is assumed to be packed next to the widget it controls, and the fill packing attribute is used to extend the scrollbar to match the length of its adjacent widget. Example 30-1 on page 430 shows how to arrange scrollbars with another widget. The message widget displays a fixed string on multiple lines, and it uses one of two attributes to constrain its size: its aspect or its width. The aspect ratio is defined to be 100*width/height, and it formats its text to honor this constraint. However, if a width is specified, it just uses that and uses as many lines (i.e., as much height) as needed. Example 29-3 on page 423 shows how message widgets display text. Table 37-1 summarizes the attributes used to specify the size for widgets:
The aspect ratio of a message widget, which is 100 times the ratio of width divided by height. Height, in text lines or screen units. Widgets: button, canvas, checkbutton, frame, label, listbox, menubutton, radiobutton, text, and toplevel. The long dimension of a scale. Orientation for long and narrow widgets: horizontal or vertical. Widgets: scale and
scrollbar.
It is somewhat unfortunate that text-oriented widgets only take character- and line-oriented dimensions. These sizes change with the font used, and if you want a precise size you might be frustrated. Both pack and grid let the widgets decide how big to be. One trick is to put each widget, such as a label, in its own frame. Specify the size you want for the frame, and then pack the label and turn off size propagation. For example: Example 37-1 Equal-sized labels.
proc EqualSizedLabels { parent width height strings args } { set l 0 foreach s $strings { frame $parent.$l -width $width -height $height pack propagate $parent.$l false pack $parent.$l -side left eval {label $parent.$l.l -text $s} $args pack $parent.$l.l -fill both -expand true incr l } } frame .f ; pack .f EqualSizedLabels .f 1i 1c {apple orange strawberry kiwi} \ -relief raised The frames $parent.$l are all created with the same size. The pack propagate command prevents these frames from changing size when the labels are packed into them later. The labels are packed with fill and expand turned on so that they fill up the fixed-sized frames around them.
Top
Table of Contents
frame .f -borderwidth 10 pack .f foreach relief {raised sunken flat ridge groove solid} { label .f.$relief -text $relief -relief $relief \ -bd 2 -padx 3 pack .f.$relief -side left -padx 4 } The three-dimensional appearance of widgets is determined by two attributes: borderWidth and relief. The borderWidth adds extra space around the edge of a widget's display, and this area can be displayed in a number of ways according to the relief attribute. The solid relief was added in Tk 8.0 to support the Macintosh look for entry widget, and it works well against white backgrounds. Macintosh and Windows buttons do not support different reliefs, and the Macintosh buttons do not honor border width. The activeBorderWidth attribute is a special case for menus. It defines the border width for the menu entries. The relief of a menu is not configurable. It probably is not worth adjusting the menu border width attributes because the default looks OK. The native menus on Windows and Macintosh do not honor this attribute. The activeRelief applies to the elements of a scrollbar (the elevator and two arrows) when the
mouse is over them. The elementBorderWidth sets the size of the relief on these elements. Changing the activeRelief does not look good. The native scrollbars on Macintosh and Windows do not honor this attribute. Table 37-2 lists the attributes for borders and relief.
The width of the border around a widget, in screen units. Widgets: button, canvas, checkbutton, entry, frame, label,
listbox, menu, menubutton, message, radiobutton, scale, scrollbar, text, and toplevel.
bd elementBorderWidth relief
Short for borderwidth. Tcl commands only. The width of the border on scrollbar and scale elements. The appearance of the border. Values: flat, raised, sunken, ridge, groove, or solid. Widgets: button, canvas, checkbutton, entry, frame, label,
listbox, menubutton, message, radiobutton, scale, scrollbar, text, and toplevel.
activeBorderWidth activeRelief
The border width for menu entries. UNIX only. The relief for active scrollbar elements. UNIX only.
Top
Table of Contents
The color of the highlight when the widget has focus. The highlight color when the widget does not have focus. The width of the highlight border.
Top
Table of Contents
The anchor position of the widget. Values: n, ne, e, se, s, sw, w, nw, or center. Widgets: button, checkbutton, label, menubutton, message, or radiobutton.
padX, padY
Padding space in the X or Y direction, in screen units. Widgets: button, checkbutton, label, menubutton, message, radiobutton, or text.
The padding attributes for a widget define space that is never occupied by the display of the widget's contents. For example, if you create a label with the following attributes and pack it into a frame by itself, you will see the text is still centered, despite the anchor attribute. Example 37-3 Padding provided by labels and buttons.
The anchor attribute only affects the display if there is extra room for another reason. One way to get extra room is to specify a width attribute that is longer than the text. The following label has rightjustified text. You can see the default padX value for labels, which is one pixel: Example 37-4 Anchoring text in a label or button.
label .foo -text Foo -width 10 -anchor e pack .foo Another way to get extra display space is with the -ipadx and -ipady packing parameters. The example in the next section illustrates this effect. Chapter 23 has several more examples of the packing parameters.
frame .f -bg white label .f.one -text One -relief raised -bd 2 -padx 3m -pady 2m
pack .f.one -side top label .f.two -text Two \ -highlightthickness 4 -highlightcolor red \ -borderwidth 5 -relief raised \ -padx 0 -pady 0 \ -width 10 -anchor nw pack .f.two -side top -pady 10 -ipady 10 -fill both focus .f.two pack .f The first label in the example uses a raised relief, so you can see the two-pixel border. (The default border size changed in Tk 8.0 to one pixel to match the CDE look and feel.) There is no highlight on a label by default. There is internal padding so that the text is spaced away from the edge of the label. The second label adds a highlight rectangle by specifying a nonzero thickness. Widgets like buttons, entries, listboxes, and text have a highlight rectangle by default. The second label's padding attributes are reduced to zero. The anchor positions the text right next to the border in the upper-left (nw) corner. However, note the effect of the padding provided by the packer. There is both external and internal padding in the Y direction. The external padding (from pack -pady) results in unfilled space. The internal packing (pack -ipady) is used by the label for its display. This is different from the label's own -pady attribute, which keeps the text away from the top edge of the widget.
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
Colors
Table 38-1 lists the resource names for color attributes. The table indicates what widgets use the different color attributes. Remember to use all lowercase and a leading dash when specifying attributes in a Tcl command.
The normal background color. All widgets. Short for background. Command line only. The normal foreground color. Widgets: button, checkbutton, entry,
label, listbox, menu, menubutton, message, radiobutton, scale, and text.
fg activeBackground
Short for foreground. Command line only. The background when a mouse button will take an action. Widgets:
button, checkbutton, menu, menubutton, radiobutton, scale, and scrollbar.
The foreground when the mouse is over an active widget. Widgets: button, checkbutton, menu, menubutton, and radiobutton. The foreground when a widget is disabled. Widgets: button,
checkbutton, menu, menubutton, and radiobutton.
The highlight color when widget does not have focus. All widgets. The highlight color when the widget has focus. All widgets. The color of the insert cursor. Widgets: canvas, entry, and text. The background of selected text. Widgets: canvas, entry, listbox, and
text.
The foreground color is used to draw an element, while the background color is used for the blank area behind the element. Text, for example, is painted with the foreground color. There are several variations on foreground and background that reflect different states for widgets or items they are displaying. Each attribute also has a resource class. This is most useful for the variations on foreground and background colors. For example, Tk does not have a reverse video mode. However, with a couple of resource specifications you can convert a monochrome display into reverse video. The definitions are given in Example 38-1. The Foreground and Background resource class names are used, and the various foreground and background colors (e.g., activeBackground) have the correct resource class so these settings work. You have to set these resources before you create any widgets: Example 38-1 Resources for reverse video. proc ReverseVideo {} { option add *Foreground white option add *Background black }
Color Palettes
The tk_setPalette command changes colors of existing widgets and installs resource values so new widgets have matching colors. If you give it a single argument, it treats this as the background and then computes new values for the other color resources. For example, if you do not like the standard Tk grey, you can lighten your spirits with a cool blue background: tk_setPalette #0088cc If you liked the light brown color scheme of Tk 3.6, you can restore that palette with the tk_bisque command: tk_bisque The tk_setPalette command can be used to change any of the color attributes. You can specify a set of name-value pairs, where the names are color resource names and the values are new color values:
Color Values
Color values are specified in two ways: symbolically (e.g., red), or by hexadecimal numbers (e.g., #ff0000 ). The leading # distinguishes the hexadecimal representation from the symbolic one. The number is divided into three equal-sized fields that give the red, green, and blue values, respectively. The fields can specify 4, 8, 12, or 16 bits of a color: #RGB #RRGGBB #RRRGGGBBB #RRRRGGGGBBBB 4 bits per color 8 bits per color 12 bits per color 16 bits per color
If you specify more resolution than is supported by the display, the low-order bits of each field are discarded. The different display types supported by Tk are described in the next section. Each field ranges from 0, which means no color, to a maximum, which is all ones in binary, or all f in hex, that means full color saturation. For example, pure red can be specified four ways: #f00 #ff0000 #fff000000 #ffff00000000 There is a large collection of symbolic color names like "red," "blue," "green," "thistle," "medium sea green," and "yellow4." These names originate from X and UNIX, and Tk supports these colors on all platforms. You can find the list in the Tk sources in the xlib/xcolor.c file. Or, run the xcolors program that comes with the standard X distribution. The Windows and Macintosh platforms have a small set of colors that are guaranteed to exist, and Tk defines names for these. The advantage of using these colors is that they are shared by all applications, so the system can manage colors efficiently. Table 38-2 lists the system colors on Windows. Several of these colors map to the same RGB value. Table 38-3 lists the system colors on Macintosh.
system3dDarkShadow system3dLight systemActiveBorder systemActiveCaption systemAppWorkspace systemBackground systemButtonFace systemButtonHighlight systemButtonShadow systemButtonText systemCaptionText systemDisabledText systemGrayText systemHighlight systemHighlightText systemInactiveBorder systemInactiveCaption systemInactiveCaptionText systemInfoBackground systemInfoText systemMenu systemMenuText systemScrollbar systemWindow systemWindowFrame systemWindowText
Dark part of button 3D-relief. Light part of button 3D-relief. Window border when activated. Caption (i.e., title bar) when activated. Background for MDI workspaces. Widget background. Button background. Lightest part of button 3D-relief. Darkest part of button 3D-relief. Button foreground. Caption (i.e., title bar) text. Text when disabled. Grey text color. Selection background. Selection foreground. Window border when not activated. Caption background when not activated. Caption text when not activated. Help pop-up background. Help pop-up text. Menu background. Menu foreground. Scrollbar background. Text window background. Text window frame. Text window text color. Table 38-3. Macintosh system colors.
systemHighlight systemHighlightText systemButtonFace systemButtonFrame systemButtonText systemWindowBody systemMenuActive systemMenuActiveText systemMenu systemMenuDisabled systemMenuText
Selection background. Selection foreground. Button background. Button frame. Button foreground. Widget background. Selected menu item background. Selected menu item foreground. Menu background. Disabled menu item background. Menu foreground.
The winfo rgb command maps from a color name (or value) to three numbers that are its red, green, and blue values. You can use this to compute variations on a color. The ColorDarken procedure shown below uses the winfo rgb command to get the red, green, and blue components of the input color. It reduces these amounts by 5 percent, and reconstructs the color specification using the format command. Example 38-2 Computing a darker color. proc ColorDarken { win color } { set rgb [winfo rgb $win $color] return [format "#%03x%03x%03x" \ [expr round([lindex $rgb 0] * 0.95)] \ [expr round([lindex $rgb 1] * 0.95)] \ [expr round([lindex $rgb 2] * 0.95)]] }
Top
Table of Contents
Greyscale with a fixed colormap defined by the system. Greyscale with a writable colormap. Color with a fixed colormap defined by the system. Color values determined by single writable colormap. Color values determined by three colormaps defined by the system: one each for red, green, and blue. Color values determined by three writable colormaps: one each for red, green, and blue. Use the best visual for a given depth.
The frame and toplevel widgets support a colormap and visual attribute. You can query these attributes on all platforms. On Windows and Macintosh there is only one visual type at a time, and users may be able to change it for their whole system. On UNIX, the X server typically supports more than one visual class on the same display, and you can create frames and toplevels that use a particular visual class. The value of the visual attribute has two parts, a visual type and the desired depth of the display. The following example requests a greyscale visual with a depth of 4 bits per pixel: toplevel .grey -visual "greyscale 4" You can start wish with a -visual command line argument: wish -visual "truecolor 24" A visual is associated with a colormap. Windows and Macintosh have a single colormap that is shared by all applications. UNIX allows for private colormaps, which can be useful if you absolutely must have lots of colors. However, the drawback of a private colormap is that the display flashes as the mouse enters windows with their own colormap. This is because the monitor hardware really only has one colormap, so the X server must swap colormaps. Macintosh and Windows manage their colormap more gracefully, although if you use too many colors some flashing can occur. Tk can simulate private colormaps on Windows, but it is probably better to let the system manage the colormap. Tk on the Macintosh always uses a 24-bit truecolor visual, which is basically unlimited colors, and lets the operating system dither colors if necessary. By default a widget inherits the colormap and visual from its parent widget. The value of the colormap attribute can be the keyword new, in which case the frame or toplevel gets a new private colormap, or it can be the name of another widget, in which case the frame or toplevel shares the colormap of that widget. When sharing colormaps, the other widget must be on the same screen and using the same visual class.
Top
Table of Contents
image create type ? name? ?options? image delete name image height name image names image type name image types image width name
Creates an image of the specified type. If name is not specified, one is made up. The remaining arguments depend on the type of image being created. Deletes the named image. Returns the height of the image, in pixels. Returns the list of defined images. Returns the type of the named image. Returns the list of possible image types. Returns the width of the image, in pixels.
The exact set of options for image create depend on the image type. There are two built-in image types: bitmap and photo. Chapter 47 describes the C interface for defining new image types.
Bitmap Images
A bitmap image has a main image and an optional mask image. The main image is drawn in the foreground color. The mask image is drawn in the background color, unless the corresponding bit is set in the main image. The remaining bits are "clear" and the widget's normal background color shows through. Table 38-6 lists the options supported by the bitmap image type:
The background color (no -bg equivalent). The contents of the bitmap as a string. The name of the file containing a bitmap definition. The foreground color (no -fg equivalent). The contents of the mask as a string. The name of the file containing the mask data.
The bitmap definition files are stylized C structure definitions that the Tk library parses. The files usually have a .xbm file name extension. These are generated by bitmap editors such as bitmap program, which comes with the standard X distribution. The -file and -maskfile options name a file that contains such a definition. The -data and -maskdata options specify a string in the same format as the contents of one of those files.
creating an image is not required. However, there are some power and flexibility with the image command, such as the ability to reconfigure a named image (e.g., for animation) that is not possible with a bitmap. Example 38-4 Specifying a bitmap for a widget. button .foo -bitmap @glyph.xbm -fg blue The @ syntax for the bitmap attribute signals that a file containing the bitmap is being specified. It is also possible to name built-in bitmaps. The predefined bitmaps are shown in the next figure along with their symbolic name. Chapter 47 describes the C interface for defining built in bitmaps. Example 38-5 The built-in bitmaps.
frame .f -bd 4; frame .g -bd 4 ; pack .f .g -side left set parent .f ; set next .g foreach name {error gray12 gray50 hourglass \ info questhead question warning} { frame $parent.$name label $parent.$name.l -text $name -width 9 -anchor w label $parent.$name.b -bitmap $name pack $parent.$name.l -side right pack $parent.$name.b -side top pack $parent.$name -side top -expand true -fill x set tmp $parent ; set parent $next ; set next $tmp }
Photo Images
The photo image type was contributed to Tk by Paul Mackerras. It displays full color images and can do dithering and gamma correction. Table 38-7 lists the attributes for photo images. These are specified in the image create photo command.
Specifies the data format for the file or data string. The contents of the photo as a base64 coded string. The name of the file containing a photo definition. A gamma correction factor, which must be greater than zero. A value greater than one brightens an image. The height, in screen units. The width of the image, in screen units. The number of shades of gray or color for the image.
The format indicates what format the data are in. The photo image supports different image formats. Tk 4.0 supports the PPM, PGM, and GIF formats. There is a C interface to define new photo formats. The CD-ROM has a "plus-patch" version of Tk that supports pixmaps and JPEG files. Normally you do not need to specify the format because the photo implementation will try all format handlers until it find one that accepts the data. An explicit format limits what handlers are tried. The format name is treated as a prefix that is compared against the names of handlers. Case is not significant in the format name. The palette setting determines how many colors or graylevels are used when rendering an image. If a single number is specified, the image is rendered in greyscale with that many shades of gray. For full color, three numbers separated by slashes specify the number of shades of red, green, and blue, respectively. The more shades you specify the more room you take up in your colormap. The photo widget will switch to a private colormap if necessary. Multiply the number of red, green, and blue shades to determine how many different colors you use. If you have an 8-bit display, there are only 256 colors available. Reasonable palette settings that do not hog the colormap include 5/5/4 and 6/6/5. You can use fewer shades of blue because the human eye is less sensitive to blue. After you create an image you can operate on it. Table 38-8 lists the image instance operations. In the table, $p is a photo image handle returned by the image create photo command. Table 38-9 lists the options available when you copy data from one image to another. The regions involved in the copy are specified by the upper-left and lower-right corners. If the lower-right corner of the source is not specified, then it defaults to the lower-right corner of the image. If the lower-right corner of the destination is not specified, then the size is determined by the area of the source. Otherwise, the source image may be cropped or replicated to fill the destination.
$p blank $p cget option $p configure ... $p copy source options $p get x y $p put data ?-to x1 y1 x2 y2? $p read file options $p redither $p write file options
Clears the image. It becomes transparent. Returns the configuration attribute option. Reconfigures the photo image attributes. Copies another image. Table 38-9 lists the copy options. Returns the pixel value at position x y. Inserts data into the image. data is a list of rows, where each row is a list of colors. Loads an image from a file. Table 38-10 lists the read options. Reapplies the dithering algorithm to the image. Saves the image to file according to options. Table 38-11 lists the write options. Table 38-9. Copy options for photo images.
Specifies the location and area in the source image. If x2 and y2 are not given, they are set to the bottom-right corner. Specifies the location and area in the destination. If x2 and y2 are not given, the size is determined by the source. The source may be cropped or tiled to fill the destination. Shrinks the destination so that its bottom right corner matches the bottom right corner of the data copied in. This has no effect if the width and height have been set for the image. Magnifies the source so each source pixel becomes a block of x by y pixels. y defaults to x if it is not specified. Reduces the source by taking every xth pixel in the X direction and every yth pixel in the Y direction. y defaults to x.
Table 38-10 lists the read options, and Table 38-11 lists the write options. The -format option is more important for writing, because the first format found is used. With reading, the format is determined automatically. If there are multiple image types that can read the same data, you may specify a read format.
Specifies the format of the data. By default, the format is determined automatically. Specifies a subregion of the source data. If x2 and y2 are not given, the size is determined by the data. Specifies the top-left corner of the new data. Shrinks the destination so that its bottom-right corner matches the bottom-right corner of the data read in. This has no effect if the width and height have been set for the image. Table 38-11. Write options for photo images.
Specifies the format of the data. Specifies a subregion of the data to save. If x2 and y2 are not given, they are set to the lower-right corner.
Top
Table of Contents
The mouse cursor. See text for sample formats. All widgets. Color for the text insert cursor. Widgets: canvas, entry, and text. Width for three dimensional appearance. Widgets: canvas, entry, and
text.
Milliseconds the cursor blinks off. (Zero disables blinking.) Widgets: canvas, entry, and text. Milliseconds the cursor blinks on. Widgets: canvas, entry, and text. Width of the text insert cursor, in screen units. Widgets: canvas, entry, and
text.
Top
Table of Contents
A foreground and background color for the cursor can be specified. Here are some example cursor specifications: $w config -cursor watch ;# stop-watch cursor $w config -cursor {gumby blue} ;# blue gumby $w config -cursor {X_cursor red white} ;# red X on white
The other form for the cursor attribute specifies a file that contains the definition of the cursor bitmap. If two file names are specified, then the second specifies the cursor mask that determines what bits of the background get covered up. Bitmap editing programs like idraw and iconedit can be used to generate these files. Here are some example cursor specification using files. You need to specify a foreground color, and if you specify a mask file, then you also need to specify a background color: $w config -cursor "@timer.xbm black" $w config -cursor "@timer.xbm timer.mask black red" The cursors shown in Example 38-6 are available on all platforms. However, on Windows and Macintosh some of the cursors are mapped to native cursors and appear differently. On Windows the following cursors are mapped to native cursors: arrow, ibeam, icon, crosshair, fleur, sb_v_double_arrow , sb_h_double_arrow , center_ptr, watch, and xterm. These additional cursors are defined on Windows: starting, size, size_ne_sw, size_ns, size_nw_se, size_we, uparrow, and wait. On Windows, use the no cursor to eliminate the cursor. On Macintosh, the following cursors are mapped to native cursors: ibeam, xterm, cross, crosshair, plus, watch, arrow. These additional cursors are defined on Macintosh: text and cross-hair.
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
Naming a Font
There are two basic ways to name a font. You can use predefined font names (e.g., system), or you can specify a set of font attributes with a platform-independent name: label .foo -text "Hello" -font {times 12 bold} In this form, the font is specified with a three element list. The first element is the font family, the second is the size, in points, and the third is a list of style parameters. The family determines the basic look, such as courier or helvetica. The complete set of style parameters are normal, bold, roman, italic, underline, and overstrike. For example, to specify both bold and italic: label .foo -text "Hello" -font {times 12 {bold italic}} The font size is points, which are 1/72 inch. Tk maintains a scale factor that maps from points to pixels. The default scale is derived from the screen resolution, and you can change it with the tk scaling command, which is described on page 582. You can specify pixel-based sizes with negative numbers. The advantage of points over pixels is that text appears about the same size regardless of the screen resolution. (This works better on Windows and Macintosh than on Unix.) However, sometimes you want to control font size relative to other widget geometry, in which case pixel-based sizes are better. An alternate way to name font attributes uses name-value pairs. These are summarized in Table 39-1. The format is less compact, but it is useful for changing part of a font configuration because you do not need to specify everything. The same specification can be made like this: label .foo -text "Hello" -font \ {-family times -size 12 -weight bold -slant italic}
The name can be times, courier, helvetica, and others returned by the font families command. The font size is given in points, which are 1/72 inch. The value is bold or normal. The value is roman or italic. If bool is true, an underline is drawn. If bool is true, an overstrike line is drawn.
Tk matches a font specification with the fonts available on your system. It will use the best possible font, but it may have to substitute some font parameters. Tk guarantees that the Times, Courier, and Helvetica families exist. It also understands the synonyms of Courier New for Courier, and Arial or Geneva for Helvetica.The font actual command returns the parameters chosen to match a font specification: font actual {times 13 bold} -family Times -size 13 -weight bold -slant roman -underline 0 -overstrike 0 The Macintosh and Windows platforms have a system-defined default size. You can get this size by specifying a size of 0 in your specification. The system font uses this: font actual system -family Chicago -size 0 -weight normal -slant roman -underline 0 - overstrike 0
Named Fonts
You can define your own names for fonts with the font create command. Creating a named font provides a level of indirection between the font parameters and the widgets that use the fonts. If you reconfigure a named font, the widgets using it will update their display automatically. This makes it easy to support a user preference for font size. For example, we can define a font name default on all platforms: font create default -family times -size 12 The default font can be made larger at any time with font configure. Widgets using the fonts will update automatically:
System Fonts
The Windows and Macintosh platforms have system-defined fonts that are used by most applications. When you query the configuration of the Tk widgets, you will see the system font names. The parameters for the system fonts can be tuned by the user via the system control panel. You can find out the attributes of the system font with font actual. These are the system fonts for each platform: The Windows platform supports system, systemfixed, ansi, ansifixed, device, oemfixed. The fixed suffix refers to a font where each character is the same size. The Macintosh platform has system and application. The UNIX platform has fixed. This is the only X font name that is guaranteed to exist. X font names are described in the next section.
Unicode Fonts
Tk does character-by-character font substitution when it displays Unicode characters. This supports mixed display of ASCII and Kanji characters, for example. The great thing about this is that you do not have to worry too much about choosing fonts in the simple case. The problem with font substitution is that it can be slow. In the worst case, Tk will query every font installed in your system to find out whether it can display a particular character. If you know you will be displaying characters in a particular character set, you can optimize your interface by specifying a font that matches what you expect to display.
Top
Table of Contents
X Font Names
Fonts can be specified with X font names on all platforms, and you must use X font names in versions of Tk before Tk 8.0. The name fixed is an example of a short X font name. Other short names might include 6x12, 9x15, or times12. However, these aliases are site-dependent. In fact, all X font names are site dependent because different fonts may be installed on different systems. The only font guaranteed to exist on the UNIX platform is named fixed. The more general form of an X font name has several components that describe the font parameters. Each component is separated by a dash, and the asterisk (*) is used for unspecified components. Short font names are system-defined aliases for these more complete specifications. Here is an example: -*-times-medium-r-normal-*-18-*-*-*-*-*-iso8859-1 The components of X font names are listed in Table 39-2 in the order in which they occur in the font specification. The table gives the possible values for the components. If there is an ellipsis (...), then there are more possibilities, too.
The most common attributes chosen for a font are its family, weight, slant, and size. The weight is usually bold or medium. The slant component is a bit cryptic, but i means italic, r means roman (i.e., normal), and o means oblique. A given font family might have an italic version, or an oblique version, but not both. Similarly, not all weights are offered by all font families. Size can be specified in pixels (i.e., screen pixels) or points. Points are meant to be independent of the screen resolution. On a 75dpi font, there are about 10 points per pixel. Note: These "points" are different than the printer points Tk uses in screen measurements. When you use X font names, the size of the font is not affected by the Tk scaling factor described on page 580. It is generally a good idea to specify just a few key components and use * for the remaining components. The X server attempts to match the font specification with its set of installed fonts, but it fails if there is a specific component that it cannot match. If the first or last character of the font name is an asterisk, then that can match multiple components. The following selects a 12-pixel times font: *times-medium-r-*-*-12* Two useful UNIX programs that deal with X fonts are xlsfonts and xfontsel. These are part of the standard X11 distribution. xlsfonts simply lists the available fonts that match a given font name. It uses the same pattern matching that the server does. Because asterisk is special to most UNIX shells, you need to quote the font name argument if you run xlsfonts from your shell. xfontsel has a graphical user interface and displays the font that matches a given font name.
proc FontWidget { args } { # args is a Tcl command if {[catch $args w]} { # Delete the font specified in args, if any set ix [lsearch $args -font] if {$ix >= 0} { set args [lreplace $args $ix [expr $ix+1]] } # This font overrides the resource database # The "fixed" font is UNIX-specific set w [eval $args {-font fixed}] } return $w } You call FontWidget like this: FontWidget button .foo -text Foo -font garbage The FontWidget procedure reverts to a default font if the widget creation command fails. It is careful to eliminate the font specified in args, if it exists. The explicit font overrides any setting from the resource database or the Tk defaults. Of course, widget creation might fail for some more legitimate reason, but that is allowed to happen in the backup case. Again, the missing font problem disappears when you use platform-independent font names, so you only need to resort to using FontWidget in early versions of Tk.
Top
Table of Contents
Font Metrics
The font metrics command returns measurement information for fonts. It returns general information about all the characters in the font: font metrics {times 10} -ascent 9 -descent 2 -linespace 11 -fixed 0 The fixed setting is true for fonts where each character fits into the same-sized bounding box. The linespace is the distance between the baselines of successive lines. The ascent and descent are illustrated in Example 39-2: Example 39-2 Font metrics.
The font measure command returns the width of a string that will be displayed in a given font. The width does not account for heavily slanted letters that overhang their bounding box, nor does it do anything special with tabs or newlines in the string.
Top
Table of Contents
Returns the actual parameters of font. Sets or queries the parameters for fontname. Defines fontname with the specified parameters. Removes the definition for the named fonts. Returns the list of font families supported on the display of win. Returns the width of text displayed in win with font. The option can be -ascent, -descent, linespace, or -fixed. Returns the names of defined fonts.
font measure font ?-displayof win? text font metrics font ?-displayof win? ? option? font names
Top
Table of Contents
Text Attributes
Layout
Table 39-4 summarizes two simple text layout attributes: justify and wrapLength. The text widget has several more layout-related attributes, and Chapter 33 describes those in detail. The two attributes described in this section apply to the various button widgets, the label, entry, and message widgets. Those widgets are described in Chapters 27, 29, and 31. The justify attribute causes text to be centered, left-justified, or right-justified. The default justification is center for all the widgets in the table, except for the entry widget, which is left-justified by default. The wrapLength attribute specifies how long a line of text is before it is wrapped onto another line. It is used to create multiline buttons and labels. This attribute is specified in screen units, however, not string length. It is probably easier to achieve the desired line breaks by inserting newlines into the text for the button or label and specifying a wrapLength of 0, which is the default.
Maximum line length for text, in screen units. Widgets: button, checkbutton, label, menubutton, and radiobutton.
Selection Attributes
Table 39-5 lists the selection-related attributes. The exportSelection attribute controls if the selection is exported for cut and paste to other widgets. The colors for selected text are set with selectForeground and selectBackground. The selection is drawn in a raised relief, and the selectBorderWidth attribute affects the 3D appearance. Choose a border width of zero to get a flat relief.
Share selection. Widgets: entry, canvas, listbox, and text. Foreground of selected text. Background of selected text. Width of 3D raised border for selection highlight.
Top
Table of Contents
Top
Table of Contents
proc Font_Select {{top .fontsel}} { global font # Create File, Font, Size, and Format menus toplevel $top -class Fontsel -bd 10 set menubar [menu $top.menubar] $top config -menu $menubar
foreach x {File Font Size Format} { set menu [menu $menubar.[string tolower $x]] $menubar add cascade -menu $menu -label $x } $menubar.file add command -label Reset -command FontReset $menubar.file add command -label OK \ -command {set font(ok) ok} $menubar.file add command -label Cancel \ -command {set font(ok) cancel} # The Fonts menu lists the available Font families. set allfonts [font families] set numfonts [llength $allfonts] set limit 20 if {$numfonts < $limit} { # Display the fonts in a single menu foreach family $allfonts { $menubar.font add radio -label $family \ -variable font(-family) \ -value $family \ -command FontUpdate } } else { # Too many fonts. Create a set of cascaded menus to # display all the font possibilities set c 0 ; set l 0 foreach family $allfonts { if {$l == 0} { $menubar.font add cascade -label $family... \ -menu $menubar.font.$c set m [menu $menubar.font.$c] incr c } $m add radio -label $family \ -variable font(-family) \ -value $family \ -command FontUpdate set l [expr ($l +1) % $limit] } } # Complete the other menus foreach size {7 8 10 12 14 18 24 36 72} { $menubar.size add radio -label $size \ -variable font(-size) \
-value $size \ -command FontUpdate } $menubar.size add command -label Other... \ -command [list FontSetSize $top] $menubar.format add check -label Bold \ -variable font(-weight) \ -onvalue bold -offvalue normal \ -command FontUpdate $menubar.format add check -label Italic \ -variable font(-slant) \ -onvalue italic -offvalue roman \ -command FontUpdate $menubar.format add check -label underline \ -variable font(-underline) \ -command FontUpdate $menubar.format add check -label overstrike \ -variable font(-overstrike) \ -command FontUpdate # FontReset initializes the font array, which causes # the radio menu entries to get highlighted. FontReset # This label displays the current font label $top.font -textvar font(name) -bd 5 # This message displays a sampler of the font. message $top.msg -aspect 1000 \ -borderwidth 10 -font fontsel \ -text " ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 !@#$%^&*()_+-=[]{};:\"''~,.<>/?\\| " # Lay out the dialog pack $top.font $top.msg -side top set f [frame $top.buttons] button $f.ok -text Ok -command {set font(ok) 1} button $f.cancel -text Cancel -command {set font(ok) 0} pack $f.ok $f.cancel -padx 10 -side left pack $f -side top # Dialog_Wait is defined in Example 36? on page 521 set font(ok) cancel
Dialog_Wait $top font(ok) destroy $top if {$font(ok) == "ok"} { return [array get font -*] } else { return {} } } # FontReset recreates a default font proc FontReset {} { catch {font delete fontsel} font create fontsel FontSet } # FontSet initializes the font array with the settings # returned by the font actual command proc FontSet {} { global font # The name is the font configuration information # with a line break so it looks nicer set font(name) [font actual fontsel] regsub -- "-slant" $font(name) "\n-slant" font(name) # Save the actual parameters after any font substitutions array set font [font actual fontsel] } # FontSetSize adds an entry widget to the dialog so you # can enter a specific font size. proc FontSetSize {top} { set f [frame $top.size -borderwidth 10] pack $f -side top -fill x label $f.msg -text "Size:" entry $f.entry -textvariable font(-size) bind $f.entry <Return> FontUpdate pack $f.msg -side left pack $f.entry -side top -fill x } # FontUpdate is called when any of the font settings # are changed, either from the menu or FontSetSize proc FontUpdate {} {
global font # The elements of font that have a leading - are # used directly in the font configuration command. eval {font configure fontsel}[array get font -*] FontSet }
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
Does not wait for the remote command to complete. Sends to the application on the same display as window. Delimits options from the interp argument. Useful if the interp begins with a dash.
The interp argument is the name of the other application. An application defines its own name when it creates its main window. The wish shell uses as its name the last component of the file name of the script. For example, when wish interprets /usr/local/bin/exmh, it sets its application name to exmh. However, if another instance of the exmh application is already running, wish chooses the name exmh #2 , and so on. If wish is not executing from a file, its name is just wish. You may have noticed wish #2 or wish #3 in your window title bars, and this reflects the fact that multiple wish applications are running on your display. If you use Tk 3.6 or earlier and your application crashes, it can forget to unregister its name. The tkinspect program has a facility to clean up these old registrations. A script can find out its own name, so you can pass names around or put them into files in order to set up communications. The tk appname command queries or changes the application name:
set myname [tk appname] tk appname aNewName In Tk 3.6 and earlier, you have to use the winfo name command to get the name of the application: set myname [winfo name .]
Tk also requires that the xhost list be empty. The xhost mechanism is the old, not-so-secure authentication mechanism in X. With xhost you allow all programs on a list of hosts to connect to your display. The problem with this is that multiuser workstations allow remote login, so essentially anybody could log in to a workstation on the xhost list and gain access to your display. The Xauthority mechanism is much stronger because it restricts access to your account, or to accounts that you explicitly give a secret token to. The problem is that even if Xauthority is set up, the user or a program can turn on xhosts and open up access to your display. If you run the xhost program with no argument, it reports the status and what hosts are on the list. The following output is generated when access control is restricted, but programs running on sage are allowed to connect to the display: exec xhost => Access control enabled: all hosts being restricted
sage This is not good enough for Tk send. It will fail because sage is on the list. I work in an environment where old scripts and programs are constantly adding things to my xhost list for reasons that are no longer valid. I developed a version of send that checks for errors and then does the following to clean out the xhost list. You have to enable access control and then explicitly remove any hosts on the list. These are reported after an initial line that says whether or not hosts are restricted: xhost - ;# enable access control in general foreach host [lrange [split [exec xhost] \n] 1 end] { exec xhost -$host ;# clear out exceptions }
Top
Table of Contents
} if {$argc > 3} { set uiCmd [lindex $argv 3] } # Read input and send it to the logger while {[gets stdin input] >= 0} { # Ignore errors with the logger catch {send $app [concat $cmd [list $input\n]]} } # Notify the controller, if any if [info exists ui] { if [catch {send $ui $uiCmd}msg] { puts stderr "send.tcl could not notify $ui\n$msg" } } # This is necessary to force wish to exit. exit The sender application supports communication with two processes. It sends all its input to a primary "logging" application. When the input finishes, it can send a notification message to another "controller" application. The logger and the controller could be the same application. Use list to quote arguments to send.
Consider the send command used in the example: send $app [concat $cmd [list $input\n]] The combination of concat and list is tricky. The list command quotes the value of the input line. This quoted value is then appended to the command, so it appears as a single extra argument. Without the quoting by list, the value of the input line will affect the way the remote interpreter parses the command. Consider these alternatives: send $app [list $cmd $input] This form is safe, except that it limits $cmd to a single word. If cmd contains a value like the ones given below, the remote interpreter will not parse it correctly. It will treat the whole multiword value as the name of a command: .log insert end
.log see end ; .log insert end This is the most common wrong answer: send $app $cmd $input The send command concatenates $cmd and $input together, and the result will be parsed again by the remote interpreter. The success or failure of the remote command depends on the value of the input data. If the input included Tcl syntax like $ or [ ], errors or other unexpected behavior would result.
Top
Table of Contents
Communicating Processes
Chapter 22 presented two examples: a browser for the examples in this book, and a simple shell in which to try out Tcl commands. In that chapter they are put into the same application. The two examples shown below hook these two applications together using the send command. Example 40-2 changes the Run and Reset procedures to send Tcl commands the shell defined in Example 22-4 on page 329. The StartEvalServer procedure starts up the shell, if necessary. Example 40-2 Hooking the browser to an eval server. # Replace the Run and Reset procedures from # Example 22? on page 324 with these procedures # Start up the evalsrv.tcl script. proc StartEvalServer {} { global browse # Start the shell and pass it our name. exec evalsrv.tcl [tk appname] & # Wait for evalsrv.tcl to send us its name tkwait variable browse(evalInterp) } proc Run {} { global browse set apps [winfo interps] set ix [lsearch -glob $apps evalsrv.tcl*] if {$ix < 0} { # No evalsrv.tcl application running StartEvalServer } if {![info exists browse(evalInterp)]} { # Hook up to already running eval server set browse(evalInterp) [lindex $apps $ix] } if [catch {send $browse(evalInterp) {info vars}}err] { # It probably died - restart it.
StartEvalServer } # Send the command asynchronously. The two # list commands foil the concat done by send and # the uplevel in EvalServe send -async $browse(evalInterp) \ [list EvalEcho [list source $browse(current)]] } # Reset the shell interpreter in the eval server proc Reset {} { global browse send $browse(evalInterp) {EvalEcho reset} } The number of lists created before the send command may seem excessive, but they are all necessary. The send command concatenates its arguments, so instead of letting it do that, we pass it a single list. Similarly, EvalServe expects a single argument that is a valid command, so list is used to construct that. The shell in Example 22-4 on page 329 has an EvalEcho procedure that we can use as the target of send. The only thing it needs is to complete the rendez-vous with the browser. When the tcl shell starts up, it sends the browser its application name. The browser passes its own name on the command line that starts the shell, so the shell knows how to talk to the browser. Example 40-3 Making the shell into an eval server. # Add this to Example 22? on page 329 if {$argc > 0} { # Check in with the browser send [lindex $argv 0] \ [list set browse(evalInterp) [tk appname]] }
Top
Table of Contents
Example 40-5 shows EvalRead that reads commands and evaluates them in an interpreter. If the interp is {} , it causes the commands to execute in the current interpreter. In this case an uplevel #0 is necessary to ensure the command is executed in the global scope. If you use interp eval to execute something in yourself, it executes in the current scope: Example 40-5 Reading commands from a socket. proc EvalRead {sock interp} { global eval errorInfo errorCode if [eof $sock] { close $sock } else { gets $sock line append eval(cmdbuf,$sock) $line\n if {[string length $eval(cmdbuf,$sock)] && \ [info complete $eval(cmdbuf,$sock)]} { set code [catch { if {[string length $interp] == 0} { uplevel #0 $eval(cmdbuf,$sock) } else { interp eval $interp $eval(cmdbuf,$sock) } } result] set reply [list $code $result $errorInfo \ $errorCode]\n # Use regsub to count newlines set lines [regsub -all \n $reply {} junk] # The reply is a line count followed # by a Tcl list that occupies that number of lines puts $sock $lines puts -nonewline $sock $reply flush $sock set eval(cmdbuf,$sock) {} } } } Example 40-6 presents Eval_Open and Eval_Remote that implement the client side of the eval connection. Eval_Open connects to the server and returns a token, which is just the socket. The main task of Eval_Remote is to preserve the information generated when the remote command raises an error The network protocol is line-oriented. The Eval_Remote command writes the command on the socket. The EvalRead procedure uses info complete to detect the end of the command. The reply is more arbitrary, so server sends a line count and that number of lines. The regsub command counts up all the newlines because it returns the number of matches it finds. The reply is a list of error codes, results, and trace information. These details of the return command are described on page 80. Example 40-6 The client side of remote evaluation.
proc Eval_Open {server port} { global eval set sock [socket $server $port] # Save this info for error reporting set eval(server,$sock) $server:$port return $sock } proc Eval_Remote {sock args} { global eval # Preserve the concat semantics of eval if {[llength $args] > 1} { set cmd [concat $args] } else { set cmd [lindex $args 0] } puts $sock $cmd flush $sock # Read return line count and the result. gets $sock lines set result {} while {$lines > 0} { gets $sock x append result $x\n incr lines -1 } set code [lindex $result 0] set x [lindex $result 1] # Cleanup the end of the stack regsub "\[^\n]+$" [lindex $result 2] \ "*Remote Server $eval(server,$sock)*" stack set ec [lindex $result 3] return -code $code -errorinfo $stack -errorcode $ec $x } proc Eval_Close {sock} { close $sock } If an error occurs in the remote command, then a stack trace is returned. This includes the command used inside EvalRead to invoke the command, which is either the uplevel or interp eval command. This is the very last line in the stack that is returned, and regsub is used to replace this with an indication of where control transferred to the remote server: catch [Eval_Remote sock6 set xx] => 1 set errorInfo => can't read "xx": no such variable while executing "set xx
" ("uplevel" body line 1) invoked from within *Remote Server sage:4000* invoked from within "catch [Eval_Remote sock6 set xx]"
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
The wm Command
The wm command has about 20 operations that interact with the window manager. The general form of the command is: wm operation win ?args? In all cases the win argument must be a toplevel. Otherwise, an error is raised. In many cases, the operation either sets or queries a value. If a new value is not specified, then the current settings are returned. For example, this command returns the current window geometry: wm geometry . => 300x200+327+20 This command defines a new geometry: wm geometry . 400x200+0+0 There are lots of wm operations, and this reflects the complex protocol with UNIX window managers. The summary below lists the subset of operations that I find useful. The operations can be grouped into four main categories: Size, placement and decoration of windows. Use the geometry and title operations to position windows and set the title bar. Icons. Use the iconify, deiconify, and withdraw operations to open and close windows. On UNIX, closed windows are represented by an icon. Long-term session state. Use the protocol operation to get a callback when users destroy windows.
Miscellaneous. Use the transient and overrideredirect operation to get specialized windows. Future versions of Tk may support a style operation to select different kinds of toplevel windows.
Some window managers insist on having the user position windows. The sizefrom and positionfrom operations let you pretend that the user specified the size and position in order to work around this restriction. Table 41-1 summarizes the wm commands that deal with size, decorations, placement:
Constrains win's ratio of width to height to be between (a/b and c/d). Queries or sets the geometry of win. Queries or sets the grid size. w and h are the base size, in grid units. dx and dy are the size, in pixels, of a grid unit. Constrains the maximum size of win. Constrains the minimum size of win. Queries or sets who to be program or user. Queries or sets ability to resize interactively. Queries or sets who to be program or user. Queries or sets the window title to string.
Icons
UNIX window managers let you close a window and replace it with an icon. The window still exists in your application, and users can open the window later. You can open and close a window yourself with the deiconify and iconify operations, respectively. Use the withdraw operation to unmap the window without replacing it with an icon. The state operation returns the current state, which is one of normal, iconified, or withdrawn. If you withdraw a window, you can restore it to the normal state with deiconify. Windows and Macintosh do not implement icons for program windows. Instead, icons represent files and applications in the desktop environment. Tk does not provide facilities to set up desktop icons. When you iconify under Windows, the window gets minimized and users can open it by clicking on the taskbar at the bottom of the screen. When you iconify under Macintosh, the window simply gets withdrawn from the screen. Windows and Macintosh applications have an additional state, maximized, which is not yet supported by Tk. This is a full-screen display mode. Future versions of Tk may support this state. You can set the attributes of UNIX icons with the iconname, iconposition, iconbitmap, and
iconmask operations. The icon's mask is used to get irregularly shaped icons.
Chapter 38 describes how masks and bitmaps are defined. In the case of an icon, it is most likely that you have the definition in a file, so your command will look like this: wm iconbitmap . @myfilename Table 41-2 summarizes the wm operations that have to do with icons:
Opens the window win. Queries or defines the bitmap for the icon. UNIX. Closes the window win. Queries or defines the mask for the icon. UNIX. Queries or sets the name on the icon. UNIX. Queries or sets the location of the icon. UNIX. Queries or specifies an alternate window to display when in the iconified state. UNIX. Returns normal, iconic, or withdrawn. Unmaps the window. No icon is displayed.
Session State
The window manager lets users delete windows with a close operation. When the main Tk window gets deleted, wish normally quits. If you have any special processing that must take place when the user deletes a window, you need to intercept the close action. Use the wm protocol operation to register a command that handles the WM_DELETE_WINDOW message from the window manager. This works on all platforms even though "delete" is a UNIX term and "close" is the Windows and Macintosh term: wm protocol . WM_DELETE_WINDOW Quit If you intercept close on the main Tk window (i.e., dot), you must eventually call exit to actually stop your application. However, you can also take the time to prompt the user about unsaved changes, or even let the user change their mind about quitting. Other window manager messages that you can intercept are WM_SAVE_YOURSELF and WM_TAKE_FOCUS. The first is called periodically by some UNIX session managers, which are described below. The latter
is used in the active focus model. Tk (and this book) assumes a passive focus model where the window manager assigns focus to a top-level window. Saving session state.
Some UNIX window managers support the notion of a session that lasts between runs of the window system. A session is implemented by saving state about the applications that are running, and using this information to restart the applications when the window system is restarted. An easy way to participate in the session protocol is to save the command used to start your application. The wm command operation does this. The wish shell saves this information, so it is just a matter of registering it with the window manager. argv0 is the command, and argv is the commandline arguments: wm command . "$argv0 $argv" If your application is typically run on a different host than the one with the display (like in an Xterminal environment), then you also need to record what host to run the application on. Use the wm client operation for this. You might need to use hostname instead of uname on your system: wm client . [exec uname -n] Table 41-3 describes the session-related window manager operations
Records the hostname in the WM_CLIENT_MACHINE property. UNIX. Records the start-up command in the WM_COMMAND property. UNIX. Registers a command to handle the protocol request name, which can be WM_DELETE_WINDOW, WM_SAVE_YOURSELF, or WM_TAKE_FOCUS.
Miscellaneous
The UNIX window managers work by reparenting an application's window so that it is a child of the window that forms the border and decorative title bar. The wm frame operation returns the window ID of the new parent, or the ID of the window itself if it has not been reparented. The wm overrideredirect operation can set a bit that overrides the reparenting. This means that no title or
border will be drawn around the window, and you cannot control the window through the window manager. The wm group operation defines groups of windows so that the window manager can open and close them together. One window, typically the main window, is chosen as the leader. The other members of the group are iconified when it is iconified. This is not implemented on Windows and Macintosh, and not all UNIX window managers implement this, either. The wm transient operation informs the window manager that this is a temporary window and there is no need to decorate it with the border and decorative title bar. This is used, for example, on pop-up menus. On Windows, a transient window is a toolbar window that does not appear in the task bar. On Macintosh, the unsupported1 command, which is described on page 417, lets you create different styles of top-level windows. Table 41-4 lists the remaining window manager operations:
Sets or queries the WM_COLORMAP_WINDOWS property that orders windows with different colormaps. Sets or queries the focus model: active or passive. (Tk assumes the passive model.) Returns the ID of the parent of win if it has been reparented; otherwise, returns the ID of win. Queries or sets the group leader (a toplevel) for win. The window manager may unmap all the group at once. Sets or queries the override redirect bit that suppresses reparenting by the window manager. Queries or marks a window as a transient window working for leader, another widget.
Top
Table of Contents
catch {send $app [list Iam [tk appname]]} } Table 41-5 summarizes these commands:
Queries or sets the name used with send. Also returns the name used for send, for backward compatibility with Tk 3.6 and earlier. Returns the last component of pathname. Returns the list of registered Tk applications on the same display as win.
Family Relationships
The Tk widgets are arranged in a hierarchy, and you can use the winfo command to find out about the structure of the hierarchy. The winfo children operation returns the children of a window, and the winfo parent operation returns the parent. The parent of the main window is null (i.e., an empty string). A widget is also a member of a class, which is used for bindings and as a key into the resource database. The winfo class operation returns this information. You can test for the existence of a window with winfo exists, and whether or not a window is mapped onto the screen with winfo viewable. Note that winfo ismapped is true for a widget that is managed by a geometry manager, but if the widget's top-level window is not mapped, then the widget is not viewable. The winfo manager operation tells you what geometry manager is controlling the placement of the window. This returns the name of the geometry manager command. Examples include pack, place, grid, canvas, and text. The last two indicate the widget is embedded into a canvas or text widget. Table 41-6 summarizes these winfo operations:
winfo children win winfo class win winfo exists win winfo ismapped win winfo manager win winfo parent win winfo viewable win
Returns the list of children widgets of win. Returns the resource class of win. Returns 1 if win exists. Returns 1 if win is mapped onto the screen. Geometry manager: pack, place, grid, canvas, or text. Returns the parent widget of win. Returns 1 if win and all its parent windows are mapped.
Size
The winfo width and winfo height operations return the width and height of a window, respectively. Alternatively, you can ask for the requested width and height of a window. Use winfo reqwidth and winfo reqheight for this information. The requested size may not be accurate, however, because the geometry manager may allocate more or less space, and the user may resize the window. Size is not valid until a window is mapped.
A window's size is not set until a geometry manager maps a window onto the display. Initially, a window starts out with a width and height of 1. You can use tkwait visibility to wait for a window to be mapped before asking its width or height, or you can use update to give Tk a chance to update the display. There are some potential problems with update that are discussed on page 522. Dialog_Wait in Example 36-1 on page 519 uses tkwait visibility . The winfo geometry operation returns the size and position of the window in the standard geometry format: WxH+X+Y. In this case the X and Y offsets are relative to the parent widget, or relative to the root window in the case of the main window. You can find out how big the display is, too. The winfo screenwidth and winfo screenheight operations return this information in pixels. The winfo screenmmwidth and winfo screenmmheight return this information in millimeters. You can convert between pixels and screen distances with the winfo pixels and winfo fpixels operations. Given a number of screen units such as 10m, 3c, or 72p, these return the corresponding number of pixels. The first form rounds to a whole number, while the second form returns a floating point number. The correspondence between pixels and sizes may not be accurate because users can adjust the pixel size on their monitors, and Tk has no way of knowing about that. Chapter 37 explains screen units on page 526. For example: set pixelsToInch [winfo pixels . 2.54c]
Converts num, in screen units, to pixels. Returns a floating point number. Returns the geometry of win, in pixels and relative to the parent in the form WxH+X+Y Returns the height of win, in pixels. Converts num to a whole number of pixels. Returns the requested height of win, in pixels. Returns the requested width of win, in pixels. Returns the height of the screen, in pixels. Returns the height of the screen, in millimeters. Returns the width of the screen, in millimeters. Returns the width of the screen, in pixels. Returns the width of win, in pixels.
Location
winfo containing ?-displayof win? win x y winfo pointerx win winfo pointery win winfo pointerxy win winfo rootx win winfo rooty win winfo screen win winfo server win winfo toplevel win winfo x win winfo y win
Returns the pathname of the window at x and y. Returns the X screen coordinate of the mouse. Returns the Y screen coordinate of the mouse. Returns the X and Y coordinates of the mouse. Returns the X screen position of win. Returns the Y screen position of win. Returns the display identifier of win's screen. Returns the version string of the display server. Returns pathname of toplevel that contains win. Returns the X position of win in its parent. Returns the Y position of win in its parent.
The winfo x and winfo y operations return the position of the upper-left corner of a window relative to its parent widget. In the case of the main window, this is its location on the screen. The winfo rootx and winfo rooty return the screen location of the upper-left corner of a widget, even if it is not a toplevel. The winfo containing operation returns the pathname of the window that contains a point on the screen. This is useful in implementing menus and drag-and-drop applications. The winfo toplevel operation returns the pathname of the toplevel that contains a widget. If the window is itself a toplevel, then this operation returns its own pathname. The winfo screen operation returns the display identifier for the screen of the window.
The winfo vrootx and winfo vrooty are used to map from the coordinates in the virtual root
window to screen-relative coordinates. These operations return 0 if there is no virtual root window. Otherwise, they return a negative number. If you add this number to the value returned by winfo x or winfo y , it gives the screen-relative coordinate of the window: set screenx [expr [winfo x $win] + [winfo vrootx $win]] Table 41-9 summarizes these operations:
Returns the height of the virtual root window for win. Returns the width of the virtual root window for win. Returns the X position of win in the virtual root. Returns the Y position of win in the virtual root.
The id operation is useful if you need to embed another application into your window hierarchy. Wish takes a -use id command-line argument that causes it to use an existing window for its main window. Other toolkits provide similar functionality. For example, to embed another Tk app in a frame: frame .embed -container true exec wish -use [winfo id .embed] otherscript.tcl
Returns the 32-bit identifier for the atom name. Returns the atom that corresponds to the 32-bit ID. Returns the window ID of win. Returns the Tk pathname of the window with id, or null.
winfo cells win winfo colormapfull win winfo depth win winfo rgb win color winfo screencells win winfo screendepth win winfo screenvisual win winfo visual win winfo visualsavailable win
Returns the number of colormap cells in win's visual. Returns 1 if the last color allocation failed. Returns the number of bits per pixel for win. Returns the red, green, and blue values for color. Returns the number of colormap cells in the default visual. Returns the number of bits per pixel in the screen's default visual. Returns the default visual of the screen. Returns the visual class of win. Returns a list of pairs that specify the visual type and bits per pixel of the available visual classes.
Top
Table of Contents
The tk Command
The tk command provides a few miscellaneous entry points into the Tk library. The appname operation is used to set or query the application name used with the Tk send command. If you define a new name and it is already in use by another application, (perhaps another instance of yourself), then a number is appended to the name (e.g., #2, #3, and so on). This is the syntax of the command: tk appname ?name? Fonts, canvas items, and widget sizes use screen units that are pixels, points, centimeters, millimeters, or inches. There are 72 points per inch. The tk scaling command, which was added in Tk 8.0, is used to set or query the mapping between pixels and points. A scale of 1.0 results in 72 pixels per inch. A scale of 1.25 results in 90 pixels per inch. This gives accurate sizes on a 90 dpi screen or it makes everything 25% larger on a 72 dpi screen. Changing the scale only affects widgets created after the change. This is the syntax of the command: tk scaling ?num?
Top
Table of Contents
Part V. Tk Details
Top
Table of Contents
App-Defaults Files
We will assume that it is sufficient to have two sources of application defaults: a per-application database and a per-user database. In addition, we will allow for some resources to be specific to color and monochrome displays. The following example initializes the preference package by reading in the per-application and per-user resource specification files. There is also an initialization of the global array pref that will be used to hold state information about the preferences package. The Pref_Init procedure is called like this: Pref_Init $library/foo-defaults ~/.foo-defaults We assume $library is the directory holding support files for the foo application, and that per-user defaults will be kept in ~/.foo-defaults. These are UNIX-oriented file names. When you write cross-platform Tk applications, you will find that some file names are inherently platform-specific. The platform-independent operations described in Chapter 9 are great, but they do not change the fact that user preferences may be stored in c:/webtk/userpref.txt on Windows, Hard Disk:System:Preferences:WebTk Prefs on Macintosh, and ~/.webtk on UNIX. I find it useful to have a small amount of platform-specific startup code that defines these pathnames. The preference package uses resource files that work on all platforms: Example 42-1 Preferences initialization. proc Pref_Init { userDefaults appDefaults } { global pref set pref(uid) 0 ;# for a unique identifier for widgets set pref(userDefaults) $userDefaults set pref(appDefaults) $appDefaults PrefReadFile $appDefaults startup if [file exists $userDefaults] { PrefReadFile $userDefaults user } }
proc PrefReadFile { basename level } { if [catch {option readfile $basename $level}err] { Status "Error in $basename: $err" } if {[string match *color* [winfo visual .]]} { if [file exists $basename-color] { if [catch {option readfile \ $basename-color $level}err] { Status "Error in $basename-color: $err" } } } else { if [file exists $basename-mono] { if [catch {option readfile $basename-mono \ $level}err] { Status "Error in $basename-mono: $err" } } } } The PrefReadFile procedure reads a resource file and then looks for another file with the suffix color or -mono depending on the characteristics of the display. With this scheme, a UNIX user puts generic settings in ~/.foo-defaults. They put color specifications in ~/.foo-defaults-color. They put specifications for black and white displays in ~/.foo-defaults-mono. You could extend PrefReadFile to allow for per-host files as well. Throughout this chapter we assume that the Status procedure displays messages to the user. It could be as simple as: proc Status { s } {puts stderr $s }
Top
Table of Contents
Defining Preferences
This section describes the Pref_Add procedure that an application uses to define preference items. A preference item defines a relationship between a Tcl variable and a resource name. If the Tcl variable is undefined at the time Pref_Add is called, then it is set from the value for the resource. If the resource is not defined, then the variable is set to the default value. Hide simple data structures with Tcl procedures.
A default value, a label, and a more extensive help string are associated with each item, which is represented by a Tcl list of five elements. A few short routines hide the layout of the item lists and make the rest of the code read better: Example 42-2 Adding preference items. proc proc proc proc proc PrefVar { item } {lindex $item 0 } PrefRes { item } {lindex $item 1 } PrefDefault { item } {lindex $item 2 } PrefComment { item } {lindex $item 3 } PrefHelp { item } {lindex $item 4 }
proc Pref_Add { prefs } { global pref append pref(items) $prefs " " foreach item $prefs { set varName [PrefVar $item] set resName [PrefRes $item] set value [PrefValue $varName $resName] if {$value == {}} {
# Set variables that are still not set set default [PrefDefault $item] switch -regexp -- $default { ^CHOICE { PrefValueSet $varName [lindex $default 1] } ^OFF { PrefValueSet $varName 0 } ^ON { PrefValueSet $varName 1 } default { # This is a string or numeric PrefValueSet $varName $default } } } } } The procedures PrefValue and PrefValueSet are used to query and set the value of the named variable, which can be an array element or a simple variable. The upvar #0 command sets the variable in the global scope. Example 42-3 Setting preference variables. # PrefValue returns the value of the variable if it exists, # otherwise it returns the resource database value proc PrefValue { varName res } { upvar #0 $varName var if [info exists var] { return $var } set var [option get . $res {}] } # PrefValueSet defines a variable in the global scope. proc PrefValueSet { varName value } { upvar #0 $varName var set var $value } An important side effect of the Pref_Add call is that the variables in the preference item are defined at the global scope. It is also worth noting that PrefValue will honor any existing value for a variable, so if the variable is already set at the global scope, then neither the resource value nor the default value will be used. It is easy to change PrefValue to always set the variable if this is not the behavior you want. Here is a sample call to Pref_Add:
Example 42-4 Using the preferences package. Pref_Add { {win(scrollside) scrollbarSide {CHOICE left right} "Scrollbar placement" "Scrollbars can be positioned on either the left or right side of the text and canvas widgets."} {win(typeinkills) typeinKills OFF "Type-in kills selection" "This setting determines whether or not the selection is deleted when new text is typed in."} {win(scrollspeed) scrollSpeed 15 "Scrolling speed" "This parameter affects the scrolling rate when a selection is dragged off the edge of the window. Smaller numbers scroll faster, but can consume more CPU."} } Any number of preference items can be specified in a call to Pref_Add. The list-of-lists structure is created by proper placement of the curly braces, and it is preserved when the argument is appended to pref(items), which is the master list of preferences. In this example, Pref_Add gets passed a single argument that is a Tcl list with three elements. The Tcl variables are array elements, presumably related to the Win module of the application. The resource names are associated with the main application as opposed to any particular widget. They are specified in the database like this: *scrollbarSide: left *typeinKills: 0 *scrollSpeed: 15
Top
Table of Contents
proc Pref_Dialog {} { global pref if [catch {toplevel .pref}] { raise .pref } else { wm title .pref "Preferences" set buttons [frame .pref.but -bd 5] pack .pref.but -side top -fill x button $buttons.quit -text Dismiss \ -command {PrefDismiss} button $buttons.save -text Save \ -command {PrefSave}
button $buttons.reset -text Reset \ -command {PrefReset ; PrefDismiss} label $buttons.label \ -text "Click labels for info on each item" pack $buttons.label -side left -fill x pack $buttons.quit $buttons.save $buttons.reset \ -side right -padx 4 frame .pref.b -borderwidth 2 -relief raised pack .pref.b -fill both set body [frame .pref.b.b -bd 10] pack .pref.b.b -fill both set maxWidth 0 foreach item $pref(items) { set len [string length [PrefComment $item]] if {$len > $maxWidth} { set maxWidth $len } } set pref(uid) 0 foreach item $pref(items) { PrefDialogItem $body $item $maxWidth } } } The interface supports three different types of preference items: boolean, choice, and general value. A boolean is implemented with a checkbutton that is tied to the Tcl variable, which will get a value of either 0 or 1. A boolean is identified by a default value that is either ON or OFF. A choice item is implemented as a set of radiobuttons, one for each choice. A choice item is identified by a default value that is a list with the first element equal to CHOICE. The remaining list items are the choices, with the first one being the default choice. A regexp is used to check for CHOICE instead of using list operations. This is because Tcl 8.0 will complain if the value is not a proper list, which could happen with arbitrary values. If neither of these cases, boolean or choice, are detected, then an entry widget is created to hold the general value of the preference item: Example 42-6 Interface objects for different preference types. proc PrefDialogItem { frame item width } { global pref incr pref(uid) set f [frame $frame.p$pref(uid) -borderwidth 2] pack $f -fill x label $f.label -text [PrefComment $item] -width $width bind $f.label <1> \ [list PrefItemHelp %X %Y [PrefHelp $item]] pack $f.label -side left set default [PrefDefault $item]
if {[regexp "^CHOICE " $default]} { foreach choice [lreplace $default 0 0] { incr pref(uid) radiobutton $f.c$pref(uid) -text $choice \ -variable [PrefVar $item] -value $choice pack $f.c$pref(uid) -side left } } else { if {$default == "OFF" || $default == "ON"} { # This is a boolean set varName [PrefVar $item] checkbutton $f.check -variable $varName \ -command [list PrefFixupBoolean $f.check $varName] PrefFixupBoolean $f.check $varName pack $f.check -side left } else { # This is a string or numeric entry $f.entry -width 10 -relief sunken pack $f.entry -side left -fill x -expand true set pref(entry,[PrefVar $item]) $f.entry set varName [PrefVar $item] $f.entry insert 0 [uplevel #0 [list set $varName]] bind $f.entry <Return> "PrefEntrySet %W $varName" } } } proc PrefFixupBoolean {check varname} { upvar #0 $varname var # Update the checkbutton text each time it changes if {$var} { $check config -text On } else { $check config -text Off } } proc PrefEntrySet { entry varName } { PrefValueSet $varName [$entry get] } In this interface, when the user clicks a radiobutton or a checkbutton, the Tcl variable is set immediately. To obtain a similar effect with the general preference item, the <Return> key is bound to a procedure that sets the associated Tcl variable to the value from the entry widget. PrefEntrySet is a one-line procedure that saves us from using the more awkward binding shown below. Grouping with double quotes allows substitution of $varName, but then we must quote the square brackets to postpone command substitution: bind $f.entry <Return> "PrefValueSet $varName \[%W get\]" The binding on <Return> is done as opposed to using the -textvariable option because it interacts
with traces on the variable a bit better. With trace you can arrange for a Tcl command to be executed when a variable is changed, as in Example 42-10 on page 592. For a general preference item it is better to wait until the complete value is entered before responding to its new value. The other aspect of the user interface is the display of additional help information for each item. If there are lots of preference items, then there isn't enough room to display this information directly. Instead, clicking on the short description for each item brings up a toplevel with the help text for that item. The toplevel is marked transient so that the window manager does not decorate it: Example 42-7 Displaying the help text for an item. proc PrefItemHelp { x y text } { catch {destroy .prefitemhelp} if {$text == {}} { return } set self [toplevel .prefitemhelp -class Itemhelp] wm title $self "Item help" wm geometry $self +[expr $x+10]+[expr $y+10] wm transient $self .pref message $self.msg -text $text -aspect 1500 pack $self.msg bind $self.msg <1> {PrefNukeItemHelp .prefitemhelp} .pref.but.label configure -text \ "Click on pop-up or another label" } proc PrefNukeItemHelp { t } { .pref.but.label configure -text \ "Click labels for info on each item" destroy $t }
Top
Table of Contents
foreach item $preferences { set varName [PrefVar $item] set resName [PrefRes $item] if [info exists pref(entry,$varName)] { PrefEntrySet $pref(entry,$varName) $varName } set value [PrefValue $varName $resName] puts $out [format "%s\t%s" *${resName}: $value] } close $out set new [glob $pref(userDefaults).new] set old [file root $new] if [catch {file rename -force $new $old}err] { Status "Cannot install $new: $err" return } PrefDismiss } There is one fine point in PrefSave. The value from the entry widget for general-purpose items is obtained explicitly in case the user has not already pressed <Return> to update the Tcl variable. The interface is rounded out with the PrefReset and PrefDismiss procedures. A reset is achieved by clearing the option database and reloading it, and then temporarily clearing the preference items and their associated variables and then redefining them with Pref_Add. Example 42-9 Read settings from the preferences file. proc PrefReset {} { global pref # Re-read user defaults option clear PrefReadFile $pref(appDefaults) startup PrefReadFile $pref(userDefaults) user # Clear variables set items $pref(items) set pref(items) {} foreach item $items { uplevel #0 [list unset [PrefVar $item]] } # Restore values Pref_Add $items } proc PrefDismiss {} { destroy .pref catch {destroy .prefitemhelp} }
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Part V. Tk Details
Example 43-1 A user interface to widget bindings. proc Bind_Interface { w } { # Our state global bind set bind(class) $w # Set a class used for resource specifications set frame [toplevel .bindui -class Bindui] # Default relief option add *Bindui*Entry.relief sunken startup
option add *Bindui*Listbox.relief raised startup # Default Listbox sizes option add *Bindui*key.width 18 startup option add *Bindui*cmd.width 25 startup option add *Bindui*Listbox.height 5 startup # A labeled entry at the top to hold the current # widget name or class. set t [frame $frame.top -bd 2] label $t.l -text "Bindings for" -width 11 entry $t.e -textvariable bind(class) pack $t.l -side left pack $t.e -side left -fill x -expand true pack $t -side top -fill x bind $t.e <Return> [list Bind_Display $frame] # Command buttons button $t.quit -text Dismiss \ -command [list destroy $frame] button $t.save -text Save \ -command [list Bind_Save $frame] button $t.edit -text Edit \ -command [list Bind_Edit $frame] button $t.new -text New \ -command [list Bind_New $frame] pack $t.quit $t.save $t.edit $t.new -side right # A pair of listboxes and a scrollbar scrollbar $frame.s -orient vertical \ -command [list BindYview \ [list $frame.key $frame.cmd]] listbox $frame.key \ -yscrollcommand [list $frame.s set] \ -exportselection false listbox $frame.cmd \ -yscrollcommand [list $frame.s set] pack $frame.s -side left -fill y pack $frame.key $frame.cmd -side left \ -fill both -expand true foreach l [list $frame.key $frame.cmd] { bind $l <B2-Motion>\ [list BindDragto %x %y $frame.key $frame.cmd] bind $l <Button-2> \ [list BindMark %x %y $frame.key $frame.cmd] bind $l <Button-1> \ [list BindSelect %y $frame.key $frame.cmd] bind $l <B1-Motion> \ [list BindSelect %y $frame.key $frame.cmd] bind $l <Shift-B1-Motion> {} bind $l <Shift-Button-1> {}
} # Initialize the display Bind_Display $frame } The Bind_Interface command takes a widget name or class as a parameter. It creates a toplevel and gives it the Bindui class so that resources can be set to control widget attributes. The option add command is used to set up the default listbox sizes. The lowest priority, startup, is given to these resources so that clients of the package can override the size with their own resource specifications. At the top of the interface is a labeled entry widget. The entry holds the name of the class or widget for which the bindings are displayed. The textvariable option of the entry widget is used so that the entry's contents are available in a variable, bind(class). Pressing <Return> in the entry invokes Bind_Display that fills in the display. Example 43-2 Bind_Display presents the bindings for a widget or class. proc Bind_Display { frame } { global bind $frame.key delete 0 end $frame.cmd delete 0 end foreach seq [bind $bind(class)] { $frame.key insert end $seq $frame.cmd insert end [bind $bind(class) $seq] } } The Bind_Display procedure fills in the display with the binding information. The bind command returns the events that have bindings, and what the command associated with each event is. Bind_Display loops through this information and fills in the listboxes.
Top
Table of Contents
A single scrollbar scrolls both listboxes. The next example shows the scrollbar command from Bind_Interface and the BindYview procedure that scrolls the listboxes:
Example 43-4 Controlling a pair of listboxes with one scrollbar. scrollbar $frame.s -orient vertical \ -command [list BindYview [list $frame.key $frame.cmd]] proc BindYview { lists args } { foreach l $lists { eval {$l yview}$args } } The BindYview command is used to change the display of the listboxes associated with the scrollbar. The first argument to BindYview is a list of widgets to scroll, and the remaining arguments are added by the scrollbar to specify how to position the display. The details are essentially private between the scrollbar and the listbox. See page 429 for the details. The args keyword is used to represent these extra arguments, and eval is used to pass them through BindYview. The reasoning for using eval like this is explained in Chapter 10 on page 128. The Listbox class bindings for <Button-2> and <B2-Motion> cause the listbox to scroll as the user drags the widget with the middle mouse button. These bindings are adjusted so that both listboxes move together. The following example shows the bind commands from the Bind_Interface procedure and the BindMark and BindDrag procedures that scroll the listboxes: Example 43-5 Drag-scrolling a pair of listboxes together. bind $l <B2-Motion>\ [list BindDragto %x %y $frame.key $frame.cmd] bind $l <Button-2> \ [list BindMark %x %y $frame.key $frame.cmd] proc BindDragto { x y args } { foreach w $args { $w scan dragto $x $y } } proc BindMark { x y args } { foreach w $args { $w scan mark $x $y } } The BindMark procedure does a scan mark that defines an origin, and BindDragto does a scan dragto that scrolls the widget based on the distance from that origin. All Tk widgets that scroll support yview, scan mark, and scan dragto. Thus the BindYview, BindMark, and BindDragto procedures are general enough to be used with any set of widgets that scroll together.
Top
Table of Contents
proc Bind_New { frame } { if [catch {frame $frame.edit}f] { # Frame already created set f $frame.edit } else { foreach x {key cmd} { set f2 [frame $f.$x] pack $f2 -fill x -padx 2 label $f2.l -width 11 -anchor e pack $f2.l -side left entry $f2.e pack $f2.e -side left -fill x -expand true bind $f2.e <Return> [list BindDefine $f]
} $f.key.l config -text Event: $f.cmd.l config -text Command: } pack $frame.edit -after $frame.top -fill x } proc Bind_Edit { frame } { Bind_New $frame set line [$frame.key curselection] if {$line == {}} { return } $frame.edit.key.e delete 0 end $frame.edit.key.e insert 0 [$frame.key get $line] $frame.edit.cmd.e delete 0 end $frame.edit.cmd.e insert 0 [$frame.cmd get $line] } The -width 11 and -anchor e attributes for the label widgets are specified so that the Event: and Command: labels will line up with the Bindings for label at the top.
Top
Table of Contents
also
Example 43-7 Defining and saving bindings. proc BindDefine { f } { if [catch { bind [$f.top.e get] [$f.edit.key.e get] \ [$f.edit.cmd.e get] } err] { Status $err } else { # Remove the edit window pack forget $f.edit } } proc Bind_Save { dotfile args } { set out [open $dotfile.new w] foreach w $args { foreach seq [bind $w] { # Output a Tcl command puts $out [list bind $w $seq [bind $w $seq]] } } close $out
file rename -force $dotfile.new $dotfile } proc Bind_Read { dotfile } { if [catch { if [file exists $dotfile] { # Read the saved Tcl commands source $dotfile } } err] { Status "Bind_Read $dotfile failed: $err" } }
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Basic Concepts
This chapter assumes that you know some C or C++. You do not have to be an expert programmer to use the Tcl APIs. Indeed, one of Tcl's strengths is the ease with which you can extend it by writing C code. This chapter provides a few working examples that explain how to initialize your application and create Tcl commands. It describes how to organize your code into packages. It concludes with notes about compiling Tcl under UNIX, Windows, and Macintosh.
Getting Started
There are two ways to get started writing C code for Tcl applications. The easiest way is to write an extension that just adds some new commands to a standard Tcl shell like tclsh or wish. With this approach the Tcl shell creates a basic framework for you, and your C code just extends this framework with new commands. Tcl supports dynamic loading, so you can compile your extension as a shared library (i.e., DLL) and load it into a running Tcl shell. This is the easiest approach because the Tcl shell handles the details of startup and shutdown, and it provides an interactive console to enter Tcl commands. In the case of wish, it also provides the framework for a graphical user interface. Finally, a loadable extension can be shared easily with other Tcl users. The second way to use the Tcl library is to add it to an existing application. If your application is very simple, it may make sense to turn it into an extension for a standard Tcl shell, which brings you back to the first, simpler approach. However, if your application already has a complex framework (e.g., it is a long-running server process), then you can just add Tcl to it and export the functionality of your application as one or more Tcl commands. Once you do this, you will find that you can extend your application with all the features provided by Tcl.
here because we are really talking about the data representation of the arguments and results. We are not talking about methods and inheritance and other things associated with object oriented programming. However, the Tcl C APIs use a structure called a Tcl_Obj, which is called a dual ported object in the reference material. I prefer the term "Tcl_Obj value". The string interface is quite simple. A command procedure gets an array of strings as arguments, and it computes a string as the result. Tcl 8.0 generalized strings into the Tcl_Obj type, which can have two representations: both a string and another native representation like an integer, floating point number, list, or bytecodes. An object-based command takes an array of Tcl_Obj pointers as arguments, and it computes a Tcl_Obj as its result. The goal of the Tcl_Obj type is to reduce the number of conversions between strings and native representations. Object-based commands will be more efficient than the equivalent string-based commands, but the APIs are a little more complex. For simple tasks, and for learning, you can use just the simpler string-based command interface.
SWIG
David Beasley created a nice tool called SWIG (Simple Wrapper Interface Generator) that generates the C code that implements command procedures that expose a C or C++ API as Tcl commands. This can be a great time saver if you need to export many calls to Tcl. The only drawback is that a C interface may not feel that comfortable to the script writer. Handcrafted Tcl interfaces can be much nicer, but automatically-generated interfaces are just fine for rapid prototyping and for software testing environments. You can learn more about SWIG at its web site: http://www.swig.org/
Tcl Initialization
Before you can use your command procedures from Tcl scripts, you need to register them with Tcl. In some cases, you may also need to create the Tcl interpreter, although this is done for you by the standard Tcl shells. If you are writing an extension, then you must provide an initialization procedure. The job of this procedure is to register Tcl commands with Tcl_CreateCommand or Tcl_CreateObjCommand. This is shown in Example 44-1 on page 608. The name of this procedure must end with _Init, as in Expect_Init, Blt_Init, or Foo_Init, if you plan to create your extension as a shared library. This procedure is called automatically when the Tcl script loads your library with the load command, which is described on page 607. If you need to create the Tcl interpreter yourself, then there are two levels of APIs. At the most basic level there is Tcl_CreateInterp. This creates an interpreter that includes the standard commands listed in Table 1-4 on page 22. You still have to initialize all your custom commands (e.g., by calling Foo_Init) and arrange to run a script using Tcl_Eval or Tcl_EvalFile . However, there are a lot of details to get right, and Tcl provides a higher level interface in Tcl_Main and Tcl_AppInit. Tcl_Main creates the interpreter for you, processes command line arguments to get an initial script to run, and even provides an interactive command loop. It calls out to Tcl_AppInit, which you provide, to complete the initialization of the interpreter. The use of Tcl_Main is shown in Example 44-13 on page 629. There are even more details to get right with a Tk application because of the window system and the event loop. These details are hidden behind Tk_Main, which makes a similar call out to Tk_AppInit that you provide to complete initialization.
overview of all the procedures in the Tcl and Tk C library, but this book does not provide a complete reference. Refer to the on-line manual pages for the specific details about each procedure; they are an excellent source of information. The manual pages should be part of every Tcl distribution. They are on the book's CD, and they can be found web at: http://www.scriptics.com/man/ The Tcl source code is worth reading.
Finally, it is worth emphasizing that the source code of the Tcl C library is a great source of information. The code is well written and well commented. If you want to see how something really works, reading the code is worthwhile.
Top
Table of Contents
load random.shlib Random On Windows, load looks in the same directory as the Tcl/Tk application, the current directory, the C:\Windows\System directory (or C:\Windows\System32 on Windows NT), the C:\Windows directory, and then the directories listed in the PATH environment variable. load random.dll Random Fortunately, you usually do not have to worry about these details because the Tcl package facility can manage your libraries for you. Instead of invoking load directly, your scripts can use package require instead. The package facility keeps track of where your libraries are and knows how to call load for your platform. It is described in Chapter 12.
* Initialize the stub table interface, which is * described in Chapter 45. */ if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { return TCL_ERROR; } /* * Register two variations of random. * The orandom command uses the object interface. */ Tcl_CreateCommand(interp, "random", RandomCmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); Tcl_CreateObjCommand(interp, "orandom", RandomObjCmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); /* * Declare that we implement the random package * so scripts that do "package require random" * can load the library automatically. */ Tcl_PkgProvide(interp, "random", "1.1"); return TCL_OK; }
Using Tcl_PkgProvide
Random_Init uses Tcl_PkgProvide to declare what package is provided by the C code. This call helps the pkg_mkIndex procedure learn what libraries provide which packages. pkg_mkIndex saves this information in a package database, which is a file named pkgIndex.tcl. The package require command looks for the package database files along your auto_path and automatically loads your
package. The general process is: Create your shared library and put it into a directory listed on your auto_path variable, or a subdirectory of one of the directories on your auto_path. Run the pkg_mkIndex procedure in that directory, giving it the names of all the script files and shared libraries it should index. Now your shared library is ready for use by other scripts. A script uses package require to request a package. The correct load command for your system will be used the first time a command from your package is used. The package command is the same on all platforms: package require random => 1.1 This process is explained in more detail on page 165.
Top
Table of Contents
A C Command Procedure
Tcl 8.0 introduced a new interface for Tcl commands that is designed to work efficiently with its internal on-the-fly byte code compiler. The original interface to commands was string oriented. This resulted in a lot of conversions between strings and internal formats such as integers, double-precision floating point numbers, and lists. The new interface is based on the Tcl_Obj type that can store different types of values. Conversions between strings and other types are done in a lazy fashion, and the saved conversions help your scripts run more efficiently. This section shows how to build a random number command using both interfaces. The string-based interface is simpler, and we start with that to illustrate the basic concepts. You can use it for your first experiments with command procedures. Once you gain some experience, you can start using the interfaces that use Tcl_Obj values instead of simple strings. If you have old command procedures from before Tcl 8.0, you need to update them only if you want extra efficiency. The string and Tcl_Obj interfaces are very similar, so you should find updating your command procedures straightforward.
then Tcl passes it back to you when the command is invoked. This is especially useful with Tk widgets, which are explained in more detail in Chapter 46. Our simple RandomCmd command procedure does not use this feature, so it passes NULL into Tcl_CreateCommand. The DeleteProc is called when the command is destroyed, which is typically when the whole Tcl interpreter is being deleted. If your state needs to be cleaned up, you can do it then. RandomCmd does not use this feature, either. The arguments from the Tcl command are available as an array of strings defined by an argv parameter and counted by an argc parameter. This is the same interface that a main program has to its command line arguments. Example 44-2 shows the RandomCmd command procedure: Example 44-2 The RandomCmd C command procedure. /* * RandomCmd -* This implements the random Tcl command. With no arguments * the command returns a random integer. * With an integer valued argument "range", * it returns a random integer between 0 and range. */ int RandomCmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]) { int rand, error; int range = 0; if (argc > 2) { interp->result = "Usage: random ?range?"; return TCL_ERROR; } if (argc == 2) { if (Tcl_GetInt(interp, argv[1], &range) != TCL_OK) { return TCL_ERROR; } } rand = random(); if (range != 0) { rand = rand % range; } sprintf(interp->result, "%d", rand); return TCL_OK; } The return value of a Tcl command is really two things: a result string and a status code. The result is a string that is either the return value of the command as seen by the Tcl script, or an error message that is reported upon error. For example, if extra arguments are passed to the command procedure, it raises a Tcl error by doing this:
interp->result = "Usage: random ?range?"; return TCL_ERROR; The random implementation accepts an optional argument that is a range over which the random numbers should be returned. The argc parameter is tested to see if this argument has been given in the Tcl command. argc counts the command name as well as the arguments, so in our case argc == 2 indicates that the command has been invoked something like: random 25 The procedure Tcl_GetInt converts the string-valued argument to an integer. It does error checking and sets the interpreter's result field in the case of error, so we can just return if it fails to return TCL_OK. if (Tcl_GetInt(interp, argv[1], &range) != TCL_OK) { return TCL_ERROR; } Finally, the real work of calling random is done, and the result is formatted directly into the result buffer using sprintf. A normal return looks like this: sprintf(interp->result, "%d", rand); return TCL_OK;
sprintf(interp->result, "%d", rand); Using sprintf is suitable if you know your result string is short, which is often the case. The other way is to set interp->result to the address of a constant string. In this case, the original result buffer is not used, and there is no cleanup required because the string is compiled into the program: interp->result = "Usage: random ?random?"; In more general cases, the following procedures should be used to manage the result and freeProc fields. These procedures automatically manage storage for the result: Tcl_SetResult(interp, string, freeProc) Tcl_AppendResult(interp, str1, str2, str3, (char *)NULL) Tcl_AppendElement(interp, string)
Tcl_SetResult sets
the return value to be string. The freeProc argument describes how the result should be disposed of: TCL_STATIC is used in the case where the result is a constant string allocated by the compiler, TCL_DYNAMIC is used if the result is allocated with Tcl_Alloc, which is a platform- and compiler-independent version of malloc, and TCL_VOLATILE is used if the result is in a stack variable. In the TCL_VOLATILE case, the Tcl interpreter makes a copy of the result before calling any other command procedures. Finally, if you have your own memory allocator, pass in the address of the procedure that should free the result. copies its arguments into the result buffer, reallocating the buffer if necessary. The arguments are concatenated onto the end of the existing result, if any. Tcl_AppendResult can be called several times to build a result. The result buffer is overallocated, so several appends are efficient.
Tcl_AppendResult
adds the string to the result as a proper Tcl list element. It might add braces or backslashes to get the proper structure.
Tcl_AppendElement
If you have built up a result and for some reason want to throw it away (e.g., an error occurs), then you can use Tcl_ResetResult to restore the result to its initial state. Tcl_ResetResult is always called before a command procedure is invoked.
are stored as sequences of byte codes. Conversion between the native representation and a string are done upon demand. There are APIs for accessing Tcl_Obj values, so you do not have to worry about type conversions unless you implement a new type. Example 44-3 shows the random command procedure using the Tcl_Obj interfaces: Example 44-3 The RandomObjCmd C command procedure. /* * RandomObjCmd -* This implements the random Tcl command from * Example 44? using the object interface. */ int RandomObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *resultPtr; int rand, error; int range = 0; if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?range?"); return TCL_ERROR; } if (objc == 2) { if (Tcl_GetIntFromObj(interp, objv[1], &range) != TCL_OK) { return TCL_ERROR; } } rand = random(); if (range != 0) { rand = rand % range; } resultPtr = Tcl_GetObjResult(interp); Tcl_SetIntObj(resultPtr, rand); return TCL_OK; } Compare Example 44-2 with Example 44-3. You can see that the two versions of the C command procedures are similar. The Tcl_GetInt call is replaced with Tcl_GetIntFromObj call. This receives an integer value from the command argument. This call can avoid conversion from string to integer if the Tcl_Obj value is already an integer. The result is set by getting a handle on the result object and setting its value. This is done instead of accessing the interp->result field directly: resultPtr = Tcl_GetObjResult(interp); Tcl_SetIntObj(resultPtr, rand);
The Tcl_WrongNumArgs procedure is a convenience procedure that formats an error message. You pass in objv, the number of arguments to use from it, and additional string. The example creates this message: wrong # args: should be "random ?range?" Example 44-3 does not do anything obvious about storage management. Tcl initializes the result object before calling your command procedure and takes care of cleaning it up later. It is sufficient to set a value and return TCL_OK or TCL_ERROR. In more complex cases, however, you have to worry about reference counts to Tcl_Obj values. This is described in more detail later. If your command procedure returns a string, then you will use Tcl_SetStringObj. This command makes a copy of the string you pass it. The new Tcl interfaces that take strings also take length arguments so you can pass binary data in strings. If the length is minus 1, then the string is terminated by a NULL byte. A command that always returned "boring" would do this: resultPtr = Tcl_GetObjResult(interp); Tcl_SetStringObj(resultPtr, "boring", -1); This is a bit too boring. In practice you may need to build up the result piecemeal. With the stringbased API, you use Tcl_AppendResult. With the Tcl_Obj API you get a pointer to the result and use Tcl_AppendToObj or Tcl_AppendStringsToObj: resultPtr = Tcl_GetObjResult(interp); Tcl_AppendStringsToObj(resultPtr, "hello ", username, NULL);
int length; /* Number of bytes in the string */ Tcl_ObjType *typePtr;/* Type implementation */ union { long longValue; /* Type data */ double doubleValue; VOID *otherValuePtr; struct { VOID *ptr1; VOID *ptr2; } twoPtrValue; } internalRep; } Tcl_Obj; Each type implementation provides a few procedures like this: Tcl_GetTypeFromObj(interp, objPtr, valuePtr); Tcl_SetTypeObj(resultPtr, value); objPtr = Tcl_NewTypeObj(value); The initial reference count is zero.
The Tcl_NewTypeObj allocates storage for a Tcl_Obj and sets its reference count to zero. Tcl_IncrRefCount and Tcl_DecrRefCount increment and decrement the reference count on an object. Tcl_DecrRefCount frees the storage for Tcl_Obj when it goes to zero. The initial reference count of zero was chosen because functions like Tcl_SetObjResult automatically increment the reference count on an object. The Tcl_GetTypeFromObj and Tcl_SetTypeObj procedures just get and set the value; the reference count does not change. Type conversions are automatic. You can set a Tcl_Obj value to an integer and get back a string or double precision number later. The type implementations automatically take care of the storage for the Tcl_Obj value as it changes. Of course, if a Tcl_Obj stays the same type, then no string conversions are necessary and accesses are more efficient.
shared, then plus1 can be implemented efficiently by modifying the native representation of the integer. Otherwise, it has to make a copy of the object before modifying it: Example 44-5 The Plus1ObjCmd procedure. /* * Plus1ObjCmd -* This adds one to its input argument. */ int Plus1ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Tcl_Obj *objPtr; int i; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "value"); return TCL_ERROR; } objPtr = objv[1]; if (Tcl_GetIntFromObj(interp, objPtr, &i) != TCL_OK) { return TCL_ERROR; } if (Tcl_IsShared(objPtr)) { objPtr = Tcl_DuplicateObj(objPtr); /* refCount 0 */ Tcl_IncrRefCount(objPtr); /* refCount 1*/ } /* * Assert objPtr has a refCount of one here. * OK to set the unshared value to something new. * Tcl_SetIntObj overwrites the old value. */ Tcl_SetIntObj(objPtr, i+1); /* * Setting the result object adds a new reference, * so we decrement because we no longer care about * the integer object we modified. */ Tcl_SetObjResult(interp, objPtr); /* refCount 2*/ Tcl_DecrRefCount(objPtr); /* refCount 1*/ /* * Now only the interpreter result has a reference to objPtr. */ return TCL_OK; }
many procedures like Tcl_GetStringFromObj, Tcl_GetIntFromObj, Tcl_GetListFromObj, and so on. These all operate efficiently by returning a pointer to the native representation of the object. They will convert the object to the requested type, if necessary. The problem is that shared values can undergo type conversions that may invalidate your reference to a particular type of the value. Value references are only safe until the next Tcl_Get*FromObj call.
Consider a command procedure that takes two arguments, an integer and a list. The command procedure has a sequence of code like this: Tcl_GetListFromObj(interp, objv[1], &listPtr); /* Manipulate listPtr */ Tcl_GetIntFromObj(interp, objv[2], &int); /* listPtr may be invalid here */ If, by chance, both arguments have the same value, (e.g., 1 and 1), which is possible for a Tcl list and an integer, then Tcl will automatically arrange to share these values between both arguments. The pointers in objv[1] and objv[2] will be the same, and the reference count on the Tcl_Obj they reference will be at least 2. The first Tcl_GetListFromObj call ensures the value is of type list, and it returns a direct pointer to the native list representation. However, Tcl_GetInFromObj then helpfully converts the Tcl_Obj value to an integer. This deallocates the memory for the list representation, and now listPtr is a dang-ling pointer! This particular example can be made safe by reversing the calls because Tcl_GetIntFromObj copies the integer value: Tcl_GetIntFromObj(interp, objv[2], &int); Tcl_GetListFromObj(interp, objv[1], &listPtr); /* int is still a good copy of the value */ By the way, you should always test your Tcl_Get* calls in case the format of the value is incompatible with the requested type. If the object is not a valid list, the following command returns an error: if (Tcl_GetListFromObj(interp, obj[1], &listPtr) != TCL_OK) { return TCL_ERROR; }
Top
Table of Contents
*/ statePtr = (BlobState *)Tcl_Alloc(sizeof(BlobState)); Tcl_InitHashTable(&statePtr->hash, TCL_STRING_KEYS); statePtr->uid = 0; Tcl_CreateObjCommand(interp, "blob", BlobCmd, (ClientData)statePtr, BlobCleanup); return TCL_OK; } /* * BlobCleanup -* This is called when the blob command is destroyed. * * Side Effects: * This walks the hash table and deletes the blobs it * contains. Then it deallocates the hash table. */ void BlobCleanup(ClientData data) { BlobState *statePtr = (BlobState *)data; Blob *blobPtr; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; entryPtr = Tcl_FirstHashEntry(&statePtr->hash, &search); while (entryPtr != NULL) { blobPtr = Tcl_GetHashValue(entryPtr); BlobDelete(blobPtr, entryPtr); /* * Get the first entry again, not the "next" one, * because we just modified the hash table. */ entryPtr = Tcl_FirstHashEntry(&statePtr->hash, &search); } Tcl_Free((char *)statePtr); } Tcl_Alloc and Tcl_Free Instead of using malloc and free directly, you should use Tcl_Alloc and Tcl_Free in your code. Depending on compilation options, these procedures may map directly to the system's malloc and free, or use alternate memory allocators. The allocators on Windows and Macintosh are notoriously poor, and Tcl ships with a nice efficient memory allocator that is used instead. In general, it is not safe to allocate memory with Tcl_Alloc and free it with free, or allocate memory with malloc and free it with Tcl_Free. When you look at the Tcl source code you will see calls to ckalloc and ckfree. These are macros that either call Tcl_Alloc and Tcl_Free or Tcl_DbgAlloc and Tcl_DbgFree depending on a compile-time option. The second set of functions is used to debug memory leaks and other errors. You cannot mix these two allocators either, so your best bet is to stick with Tcl_Alloc and Tcl_Free everywhere.
PokeIx }; int result, index; if (objc == 1 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } if (((index == NamesIx || index == CreateIx) && (objc > 2)) || ((index == PokeIx || index == DeleteIx) && (objc == 4))) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } if (index == CreateIx) { return BlobCreate(interp, statePtr); } if (index == NamesIx) { return BlobNames(interp, statePtr); } if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "option blob ?arg ...?"); return TCL_ERROR; } else if (objc == 3) { valueObjPtr = NULL; } else { valueObjPtr = objv[3]; } /* * The rest of the commands take a blob name as the third * argument. Hash from the name to the Blob structure. */ entryPtr = Tcl_FindHashEntry(&statePtr->hash, Tcl_GetString(objv[2])); if (entryPtr == NULL) { Tcl_AppendResult(interp, "Unknown blob: ", Tcl_GetString(objv[2]), NULL); return TCL_ERROR; } blobPtr = (Blob *)Tcl_GetHashValue(entryPtr); switch (index) { case CommandIx: { return BlobCommand(interp, blobPtr, valueObjPtr); } case DataIx: { return BlobData(interp, blobPtr, valueObjPtr);
} case NIx: { return BlobN(interp, blobPtr, valueObjPtr); } case PokeIx: { return BlobPoke(interp, blobPtr); } case DeleteIx: { return BlobDelete(blobPtr, entryPtr); } } }
BlobDelete(Blob *blobPtr, Tcl_HashEntry *entryPtr) { Tcl_DeleteHashEntry(entryPtr); if (blobPtr->cmdPtr != NULL) { Tcl_DecrRefCount(blobPtr->cmdPtr); } if (blobPtr->objPtr != NULL) { Tcl_DecrRefCount(blobPtr->objPtr); } /* * Use Tcl_EventuallyFree because of the Tcl_Preserve * done in BlobPoke. See page 626. */ Tcl_EventuallyFree((char *)blobPtr, Tcl_Free); return TCL_OK; }
Building a List
The BlobNames procedure iterates through the elements of the hash table using Tcl_FirstHashEntry and Tcl_NextHashEntry. It builds up a list of the names as it goes along. Note that the object reference counts are managed for us. The Tcl_NewStringObj returns a Tcl_Obj with reference count of zero. When that object is added to the list, the Tcl_ListObjAppendElement procedure increments the reference count. Similarly, the Tcl_NewListObj returns a Tcl_Obj with reference count zero, and its reference count is incremented by Tcl_SetObjResult: Example 44-10 The BlobNames procedure. int BlobNames(Tcl_Interp *interp, BlobState *statePtr) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; Tcl_Obj *listPtr; Tcl_Obj *objPtr; char *name; /* * Walk the hash table and build a list of names. */ listPtr = Tcl_NewListObj(0, NULL); entryPtr = Tcl_FirstHashEntry(&statePtr->hash, &search); while (entryPtr != NULL) { name = Tcl_GetHashKey(&statePtr->hash, entryPtr); if (Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(name, -1)) != TCL_OK) { return TCL_ERROR; } entryPtr = Tcl_NextHashEntry(&search); }
It is quite possible for the command to turn around and delete the blob it is associated with! The Tcl_Preserve , Tcl_Release, and Tcl_EventuallyFree procedures are used to handle this situation. BlobPoke calls Tcl_Preserve on the blob before calling Tcl_Eval. BlobDelete calls Tcl_EventuallyFree instead of Tcl_Free. If the Tcl_Release call has not yet been made, then Tcl_EventuallyFree just marks the memory for deletion, but does not free it immediately. The memory is freed later by Tcl_Release. Otherwise, Tcl_EventuallyFree frees the memory directly and Tcl_Release does nothing. Example 44-12 shows BlobCommand and BlobPoke: Example 44-12 The BlobCommand and BlobPoke procedures. int BlobCommand(Tcl_Interp *interp, Blob *blobPtr, Tcl_Obj *objPtr) { if (objPtr != NULL) { if (blobPtr->cmdPtr != NULL) { Tcl_DecrRefCount(blobPtr->cmdPtr); } Tcl_IncrRefCount(objPtr); blobPtr->cmdPtr = objPtr; } if (blobPtr->cmdPtr != NULL) { Tcl_SetObjResult(interp, blobPtr->cmdPtr); } return TCL_OK; } int BlobPoke(Tcl_Interp *interp, Blob *blobPtr) { int result = TCL_OK; if (blobPtr->cmdPtr != NULL) { Tcl_Preserve(blobPtr); result = Tcl_EvalObj(interp, blobPtr->cmdPtr); /* * Safe to use blobPtr here */ Tcl_Release(blobPtr); /* * blobPtr may not be valid here */ } return result; } It turns out that BlobCmd does not actually use the blobPtr after calling Tcl_EvalObj, so it could get away without using Tcl_Preserve and Tcl_Release. These procedures do add some overhead: They put the pointer onto a list of preserved pointers and have to take it off again. If you are careful, you can omit these calls. However, it is worth noting the potential problems caused by evaluating arbitrary Tcl scripts!
Top
Table of Contents
You can get a direct pointer to the string you have created with Tcl_DStringValue: name = Tcl_DStringValue(&ds); There are a handful of additional procedures in the DString API that you can read about in the reference material. There are some that create lists, but this is better done with the Tcl_Obj interface (e.g., Tcl_NewListObj and friends). To some degree, a Tcl_Obj can replace the use of a Tcl_DString. For example, the Tcl_NewStringObj and Tcl_AppendToObj allocate a Tcl_Obj and append strings to it. However, there are a number of Tcl API procedures that take Tcl_DString types as arguments instead of the Tcl_Obj type. Also, for small strings, the DString interface is still more efficient because it can do less dynamic memory allocation.
system encoding. If you have data in nonstandard encodings, or need to translate into something other than the system encoding, you need to get a handle on the encoding with Tcl_GetEncoding, and free that handle later with Tcl_FreeEncoding: encoding = Tcl_GetEncoding(interp, name); Tcl_FreeEncoding(encoding); The names of the encodings are returned by the encoding names Tcl command, and you can query them with a C API, too. Windows has a quirky string data type called TCHAR, which is an 8-bit byte on Windows 95/98, and a 16-bit Unicode character on Windows NT. If you use a C API that takes an array of TCHAR, then you have to know what kind of system you are running on to use it properly. Tcl provides two procedures that deal with this automatically. Tcl_WinTCharToUf works like Tcl_ExternalToUtfDString, and Tcl_WinUtfToTChar works like Tcl_UtfToExternalDString: Tcl_WinUtfToTChar(string, -1, &ds); Tcl_WinTCharToUtf(string, -1, &ds); Finally, Tcl has several procedures to work with Unicode characters, which are type Tcl_UniChar, and UTF-8 encoded characters. Examples include Tcl_UniCharToUtf, Tcl_NumUtfChars, and Tcl_UtfToUniCharDString. Consult the reference materials for details about these procedures.
Top
Table of Contents
Tcl_Main
and Tcl_AppInit
This section describes how to make a custom main program that includes Tcl. However, the need for custom main programs has been reduced by the use of loadable modules. If you create your commands as a loadable package, you can just load them into tclsh or wish. Even if you do not need a custom main, this section will explain how all the pieces fit together. The Tcl library supports the basic application structure through the Tcl_Main procedure that is designed to be called from your main program. Tcl_Main does three things: It calls Tcl_CreateInterp to create an interpreter that includes all the standard Tcl commands like set and proc. It also defines a few Tcl variables like argc and argv. These have the command-line arguments that were passed to your application. It calls Tcl_AppInit, which is not part of the Tcl library. Instead, your application provides this procedure. In Tcl_AppInit you can register additional application-specific Tcl commands. It reads a script or goes into an interactive loop. You call Tcl_Main from your main program and provide an implementation of the Tcl_AppInit procedure: Example 44-13 A canonical Tcl main program and Tcl_AppInit. /* main.c */ #include <tcl.h> int Tcl_AppInit(Tcl_Interp *interp); /* * Declarations for application-specific command procedures */ int Plus1ObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); main(int argc, char *argv[]) {
/* * Initialize your application here. * * Then initialize and run Tcl. */ Tcl_Main(argc, argv, Tcl_AppInit); exit(0); } /* * Tcl_AppInit is called from Tcl_Main * after the Tcl interpreter has been created, * and before the script file * or interactive command loop is entered. */ int Tcl_AppInit(Tcl_Interp *interp) { /* * Tcl_Init reads init.tcl from the Tcl script library. */ if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* * Register application-specific commands. */ Tcl_CreateObjCommand(interp, "plus1", Plus1ObjCmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); Random_Init(interp); Blob_Init(interp); /* * Define the start-up filename. This file is read in * case the program is run interactively. */ Tcl_SetVar(interp, "tcl_rcFileName", "~/.mytcl", TCL_GLOBAL_ONLY); /* * Test of Tcl_Invoke, which is defined on page 635. */ Tcl_Invoke(interp, "set", "foo", "$xyz [foo] {", NULL); return TCL_OK; } The main program calls Tcl_Main with the argc and argv parameters passed into the program. These are the strings passed to the program on the command line, and Tcl_Main will store these values into Tcl variables by the same name. Tcl_Main is also given the address of the initialization procedure, which is Tcl_AppInit in our example. Tcl_AppInit is called by Tcl_Main with one argument, a handle on a newly created interpreter. There are three parts to the Tcl_AppInit procedure:
The first part initializes the various packages the application uses. The example calls Tcl_Init
to set up the script library facility described in Chapter 12. The core Tcl commands have already been defined by Tcl_CreateInterp, which is called by Tcl_Main before the call to Tcl_AppInit. The second part of Tcl_AppInit does application-specific initialization. The example registers the command procedures defined earlier in this Chapter. The third part defines a Tcl variable, tcl_RcFileName, which names an application startup script that executes if the program is used interactively. You can use your custom program just like tclsh, except that it includes the additional commands you define in your Tcl_AppInit procedure. The sample makefile on the CD creates a program named mytcl. You can compile and run that program and test random and the other commands. Tk_Main The structure of Tk applications is similar. The Tk_Main procedure creates a Tcl interpreter and the main Tk window. It calls out to a procedure you provide to complete initialization. After your Tk_AppInit returns, Tk_Main goes into an event loop until all the windows in your application have been destroyed. Example 44-14 shows a Tk_AppInit used with Tk_Main. The main program processes its own command-line arguments using Tk_ParseArgv, which requires a Tcl interpreter for error reporting. The Tk_AppInit procedure initializes the clock widget example that is the topic of Chapter 46: Example 44-14 A canonical Tk main program and Tk_AppInit. /* main.c */ #include <tk.h> int Tk_AppInit(Tcl_Interp *interp); /* * A table for command line arguments. */ char *myoption1 = NULL; int myint2 = 0; static Tk_ArgvInfo argTable[] = { {"-myoption1", TK_ARGV_STRING, (char *) NULL, (char *) &myoption1, "Explain myoption1"}, {"-myint2", TK_ARGV_CONSTANT, (char *) 1, (char *) &myint2, "Explain myint2"}, {"", TK_ARGV_END, }, }; main(int argc, char *argv[]) { Tcl_Interp *interp;
/* * Create an interpreter for the error message from * Tk_ParseArgv. Another one is created by Tk_Main. * Parse our arguments and leave the rest to Tk_Main. */ interp = Tcl_CreateInterp(); if (Tk_ParseArgv(interp, (Tk_Window) NULL, &argc, argv, argTable, 0) != TCL_OK) { fprintf(stderr, "%s\n", interp->result); exit(1); } Tcl_DeleteInterp(interp); Tk_Main(argc, argv, Tk_AppInit); exit(0); } int ClockCmd(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[]); int ClockObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]); void ClockObjDestroy(ClientData clientData); int Tk_AppInit(Tcl_Interp *interp) { /* * Initialize packages */ if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Tk_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* * Define application-specific commands here. */ Tcl_CreateCommand(interp, "wclock", ClockCmd, (ClientData)Tk_MainWindow(interp), (Tcl_CmdDeleteProc *)NULL); Tcl_CreateObjCommand(interp, "oclock", ClockObjCmd, (ClientData)NULL, ClockObjDestroy); /* * Define start-up filename. This file is read in * case the program is run interactively. */ Tcl_SetVar(interp, "tcl_rcFileName", "~/.mytcl", TCL_GLOBAL_ONLY);
return TCL_OK; }
Top
Table of Contents
Top
Table of Contents
its argument.
You should be aware that Tcl_Eval may modify the string that is passed into it as a side effect of the way substitutions are performed. If you pass a constant string to Tcl_Eval, make sure your compiler has not put the string constant into read-only memory. If you use the gcc compiler, you may need to use the -fwritable-strings option. Chapter 45 shows how to get the right compilation settings for your system.
Variations on Tcl_Eval
There are several variations on Tcl_Eval. The possibilities include strings or Tcl_Obj values, evaluation at the current or global scope, a single string (or Tcl_Obj value) or a variable number of arguments, and optional byte-code compilation. The most general string-based eval is Tcl_EvalEx, which takes a counted string and some flags:
Tcl_EvalEx(interp, string, count, flags); The flags are TCL_GLOBAL_EVAL and TCL_EVAL_DIRECT, which bypasses the byte-code compiler. For code that is executed only one time, TCL_EVAL_DIRECT may be more efficient. Tcl_GlobalEval is equivalent to passing in the TCL_GLOBAL_EVAL flag. The Tcl_VarEval procedure takes a variable number of strings arguments and concatenates them before evaluation: Tcl_VarEval(Tcl_Interp *interp, char *str, ..., NULL); takes an object as an argument instead of a simple string. The string is compiled into byte codes the first time it is used. If you are going to execute the script many times, then the Tcl_Obj value caches the byte codes for you. The general Tcl_Obj value interface to Tcl_Eval is Tcl_EvalObjEx, which takes the same flags as Tcl_EvalEx:
Tcl_EvalObj
Tcl_EvalObjEx(interp, objPtr, flags); For variable numbers of arguments, use Tcl_EvalObjv, which takes an array of Tcl_Obj pointers. This routine concatenates the string values of the various Tcl_Obj values before parsing the resulting Tcl command: Tcl_EvalObjv(interp, objc, objv);
Bypassing Tcl_Eval
In a performance-critical situation, you may want to avoid some of the overhead associated with Tcl_Eval. David Nichols showed me how to call the implementation of a C command procedure directly. The trick is facilitated by the Tcl_GetCommandInfo procedure that returns the address of the C command procedure for a Tcl command, plus its client data pointer. The Tcl_Invoke procedure is shown in Example 44-15. It is used much like Tcl_VarEval, except that each of its arguments becomes an argument to the Tcl command without any substitutions being performed. For example, you might want to insert a large chunk of text into a text widget without worrying about the parsing done by Tcl_Eval. You could use Tcl_Invoke like this: Tcl_Invoke(interp, ".t", "insert", "insert", buf, NULL); Or: Tcl_Invoke(interp, "set", "foo", "$xyz [blah] {", NULL);
No substitutions are performed on any of the arguments because Tcl_Eval is out of the picture. The variable foo gets the following literal value: $xyz [blah] { Example 44-15 shows Tcl_Invoke. The procedure is complicated for two reasons. First, it must handle a Tcl command that has either the object interface or the old string interface. Second, it has to build up an argument vector and may need to grow its storage in the middle of building it. It is a bit messy to deal with both at the same time, but it lets us compare the object and string interfaces. The string interfaces are simpler, but the object interfaces run more efficiently because they reduce copying and type conversions. Example 44-15 Calling C command procedure directly with Tcl_Invoke. #include <tcl.h> #if defined(__STDC__) || defined(HAS_STDARG) # include <stdarg.h> #else # include <varargs.h> #endif /* * Tcl_Invoke -* Directly invoke a Tcl command * * Call Tcl_Invoke somewhat like * Each arg becomes one argument * with no further substitutions */ /* VARARGS2 */ /* ARGSUSED */
int Tcl_Invoke TCL_VARARGS_DEF(Tcl_Interp *, arg1) { va_list argList; Tcl_Interp *interp; char *cmd; /* Command name */ char *arg; /* Command argument */ char **argv; /* String vector for arguments */ int argc, i, max; /* Number of arguments */ Tcl_CmdInfo info; /* Info about command procedures */ int result; /* TCL_OK or TCL_ERROR */ interp = TCL_VARARGS_START(Tcl_Interp *, arg1, argList); Tcl_ResetResult(interp);
/* * Map from the command name to a C procedure */ cmd = va_arg(argList, char *); if (! Tcl_GetCommandInfo(interp, cmd, &info)) { Tcl_AppendResult(interp, "unknown command \"", cmd, "\"", NULL); va_end(argList); return TCL_ERROR; } max = 20; /* Initial size of argument vector */
#if TCL_MAJOR_VERSION > 7 /* * Check whether the object interface is preferred for * this command */ if (info.isNativeObjectProc) { Tcl_Obj **objv; /* Object vector for arguments */ Tcl_Obj *resultPtr; /* The result object */ int objc; objv = (Tcl_Obj **) Tcl_Alloc(max * sizeof(Tcl_Obj *)); objv[0] = Tcl_NewStringObj(cmd, strlen(cmd)); Tcl_IncrRefCount(objv[0]); /* ref count == 1*/ objc = 1; /* * Build a vector out of the rest of the arguments */ while (1) { arg = va_arg(argList, char *); if (arg == (char *)NULL) { objv[objc] = (Tcl_Obj *)NULL; break; } objv[objc] = Tcl_NewStringObj(arg, strlen(arg)); Tcl_IncrRefCount(objv[objc]); /* ref count == 1*/ objc++; if (objc >= max) { /* allocate a bigger vector and copy old one */ Tcl_Obj **oldv = objv; max *= 2; objv = (Tcl_Obj **) Tcl_Alloc(max * sizeof(Tcl_Obj *)); for (i = 0 ; i < objc ; i++) { objv[i] = oldv[i]; }
Tcl_Free((char *)oldv); } } va_end(argList); /* * Invoke the C procedure */ result = (*info.objProc)(info.objClientData, interp, objc, objv); /* * Make sure the string value of the result is valid * and release our references to the arguments */ (void) Tcl_GetStringResult(interp); for (i = 0 ; i < objc ; i++) { Tcl_DecrRefCount(objv[i]); } Tcl_Free((char *)objv); return result; } #endif argv = (char **) Tcl_Alloc(max * sizeof(char *)); argv[0] = cmd; argc = 1; /* * Build a vector out of the rest of the arguments */ while (1) { arg = va_arg(argList, char *); argv[argc] = arg; if (arg == (char *)NULL) { break; } argc++; if (argc >= max) { /* allocate a bigger vector and copy old one */ char **oldv = argv; max *= 2; argv = (char **) Tcl_Alloc(max * sizeof(char *)); for (i = 0 ; i < argc ; i++) { argv[i] = oldv[i]; } Tcl_Free((char *) oldv); } } va_end(argList);
/* * Invoke the C procedure */ result = (*info.proc)(info.clientData, interp, argc, argv); /* * Release the arguments */ Tcl_Free((char *) argv); return result; } This version of Tcl_Invoke was contributed by Jean Brouwers. He uses TCL_VARARGS_DEF and TCL_VARARGS_START macros to define procedures that take a variable number of arguments. These standard Tcl macros hide the differences in the way you do this on different operating systems and different compilers. It turns out that there are numerous minor differences between compilers that can cause portability problems in a variety of situations. Happily, there is a nice scheme used to discover these differences and write code in a portable way. This is the topic of the next chapter.
Top
Table of Contents
Top
Table of Contents
The root of the Tcl sources. This contains a README and license_terms file, and several subdirectories. This contains .c files that implement procedures that are otherwise broken in the standard C library on some platforms. They are only used if necessary. This contains the reference documentation. Currently this is in nroff format suitable for use with the UNIX man program. The goal is to convert this to XML. This contains the generic .c and .h source files that are shared among Unix, Windows, and Macintosh. This contains the .c and .h source files that are specific to Macintosh. It also contains Code Warrior project files. This contains init.tcl and other Tcl files in the standard Tcl script library. This contains the Unicode conversion tables. There are several subdirectories (e.g., http2.0) that contain Tcl script packages.
tcl8.2/doc
This contains the Tcl test suite. These are Tcl scripts that exercise the Tcl implementation. This is a collection of scripts used to help build the Tcl distribution. This contains the .c and .h source files that are specific to UNIX. This also contains the configure script and the Makefile.in template. This contains test files for dynamic loading. These can be used to build Tcl for several different platforms. You create the package directories yourself. This contains the .c and .h source files that are specific to Windows. This also contains the configure script and the Makefile.in template. This may contain a makefile.vc that is compatible with nmake.
This contains platform-specific applications. On Windows, this also contains binary libraries (i.e., DLLs). Typical arch names are solaris-sparc, linux-ix86, and winix86. This contains platform-specific binary libraries on UNIX systems (e.g., libtcl8.2.so ) This contains platform-independent applications (e.g., Tcl script applications). This contains documentation. This contains public .h files This contains subdirectories for platform-independent script packages. Packages stored here are found automatically by the Tcl auto loading mechanism described in Chapter 12. This contains the contents of the tcl8.2/library source directory, including subdirectories. This contains Tcl scripts for package. Example package directories include tk8.2 and itcl3.0.1. This contains reference documentation in UNIX man format.
lib/tcl8.2
lib/package man
If you are an expert in configure, you may be aware of other options that give you even finer control over where the different parts of the installation go. However, because of the way Tcl automatically searches for scripts and binary libraries, you should avoid deviating from the recommended structure.
Top
Table of Contents
The developer of a source code package creates a configure.in template that expresses the system dependencies of the source code. They use the autoconf program to process this template into a configure script. The developer also creates a Makefile.in template. Creating these templates is described later. The Tcl and Tk source distributions already contain the configure
script, which can be found in the unix and win subdirectories. However, if you get the Tcl sources from the network CVS repository, you must run autoconf yourself to generate the configure script. A user of a source code package runs configure on the computer system they will use to compile the sources. This step converts Makefile.in to a Makefile suitable for the platform and configuration settings. If you only have one platform, simply run configure in the unix (or win) directory: % cd /usr/local/src/tcl8.2/unix % ./configure flags The configure flags are described in Table 45-3. I use ./configure because I do not have . on my PATH. Furthermore, I want to ensure I run the configure script from the current directory! If you build for multiple platforms, create subdirectories of unix and run configure from there. For example, here we use ../configure: % % % % cd /usr/local/src/tcl8.2/unix mkdir solaris cd solaris ../configure flags
The configure script uses the Makefile.in template to generate the Makefile. Once configure is complete, you build your program with make: % make You can do other things with make. To run the test suite, do: % make test To install the compiled program or extension, do: % make install The Tcl Extension Architecture defines a standard set of actions, or make targets, for building Tcl sources. Table 45-4 on page 652 shows the standard make targets. Make sure you have a working compiler.
As the configure script executes, it prints out messages about the properties of the current platform. You can tell if you are in trouble if the output contains either of these messages: checking for cross compiler ... yes or checking if compiler works ... no Either of these means configure has failed to find a working compiler. In the first case, it assumes you are configuring on the target system but will cross-compile from a different system. Configure proceeds bravely ahead, but the resulting Makefile is useless. While cross-compiling is common on embedded processors, it is rarely necessary on UNIX and Windows. I only see this message when my UNIX environment isn't set up right to find the compiler. Many UNIX venders no longer bundle a working compiler. Fortunately, the freely available gcc compiler has been ported to nearly every UNIX system. You should be able search the Internet and find a ready to use gcc package for your platform. On Windows there is a more explicit compiler check, and configure exits if it cannot find the compiler. Tcl is built with the Microsoft Visual C++ compiler. It ships with a batch file, vcvars32.bat , that sets up the environment so you can run the compiler from the command line. You should read that file and configure your environment so you do not have to remember to run the batch file all the time. If you use a different compiler on Windows, make sure you configure your environment so it can be run from the DOS prompt.
--prefix=dir --execprefix=dir --enable-gcc --disable-shared --enable-symbols --enable-threads --with-tcl=dir --with-tk=dir --withtclinclude=dir --with-tcllib=dir --withx11include=dir --with-x11lib=dir
This defines the root of the installation directory hierarchy. The default is /usr/local. This defines the root of the installation area for platform-specific files. This defaults to the --prefix value. An example setting is /usr/local/solarissparc.
Use the gcc compiler instead of the default system compiler. Disable generation of shared libraries and Tcl shells that dynamically link against them. Statically linked shells and static archives are built instead. Compile with debugging symbols. Compile with thread support turned on. This specifies the location of the build directory for Tcl. This specifies the location of the build directory for Tk. This specifics the directory that contains tcl.h. This specifies the directory that contains the Tcl binary library (e.g., libtclstubs.a). This specifics the directory that contains X11.h. This specifies the directory that contains the X11 binary library (e.g., libX11.6.0.so).
Any flag with disable or enable in its name can be inverted. Table 45-3 lists the non-default setting, however, so you can just leave the flag out to turn it off. For example, when building Tcl on Solaris with the gcc compiler, shared libraries, debugging symbols, and threading support turned on, use this command: configure --prefix=/home/welch/install \ --exec-prefix=/home/welch/install/solaris \ --enable-gcc --enable-threads --enable-symbols Keep all your sources next to the Tcl sources.
Your builds will go the most smoothly if you organize all your sources under a common directory. In this case, you should be able to specify the same configure flags for Tcl and all the other extensions you will compile. In particular, you must use the same --prefix and --exec-prefix so everything gets installed together.
If your source tree is not adjacent to the Tcl source tree, then you must use --with-tclinclude or -with-tcllib so the header files and runtime library can be found during compilation. Typically this can happen if you build an extension under your home directory, but you are using a copy of Tcl that has been installed by your system administrator. The --with-x11include and --with-x11lib flags are similar options necessary when building Tk if your X11 installation is in a nonstandard location.
Installation
The --prefix flag specifies the main directory (e.g., /home/welch/install). The directories listed in Table 45-2 are created under this directory. If you do not specify --exec-prefix, then the platformspecific binary files are mixed into the main bin and lib directories. For example, the tclsh8.2 program and libtcl8.2.so shared library will be installed in: /home/welch/install/bin/tclsh8.2 /home/welch/install/lib/libtclsh8.2.so The script libraries and manual pages will be installed in: /home/welch/install/lib/tcl8.2 /home/welch/install/man If you want to have installations for several different platforms, then specify an --exec-prefix that is different for each platform. For example, if you use --exec-prefix=/home/welch/install/solaris, then the tclsh8.2 program and libtcl8.2.so shared library will be installed in: /home/welch/install/solaris/bin/tclsh8.2 /home/welch/install/solaris/lib/libtclsh8.2.so The script libraries and manual pages will remain where they are, so they are shared by all platforms. Note that Windows has a slightly different installation location for binary libraries. They go into the arch/bin directory along with the main executable programs.
Top
Table of Contents
similar in spirit to Tcl_PkgRequire in that you request a minimum Tcl version number. Stubs have been supported since Tcl 8.1, and the API will evolve in a backward-compatible way. Unless your extension uses new C APIs introduced in later versions, you should specify the lowest version possible so that it is compatible with more Tcl applications.
Top
Table of Contents
Using autoconf
Autoconf uses the m4 macro processor to translate the configure.in template into the configure script. The configure script is run by /bin/sh (i.e., the Bourne Shell). Creating the configure.in template is simplified by a standard m4 macro library that is distributed with autoconf. In addition, a Tcl distribution contains a tcl.m4 file that has additional autoconf macros. Among other things, these macros support the standard configure flags described in Table 45-3. Creating configure templates can be complex and confusing. Mostly this is because a macro processor is being used to create a shell script. The days I have spent trying to change the Tcl configuration files really made me appreciate the simplicity of Tcl! Fortunately, there is now a standard set of Tcl-specific autoconf macros and a sample Tcl extension that uses them. By editing the configure.in and Makefile.in sample templates, you can ignore the details of what is happening under the covers.
Makefile Templates
Autoconf implements yet another macro mechanism for the Makefile.in templates. The basic idea is that the configure script sets shell variables as it learns things about your system. Finally, it substitutes these variables into Makefile.in to create the working Makefile. The syntax for the substitutions in Makefile.in is: @configure_variable_name@ Often the make variable and the shell variable have the same name. For example, the following statement in Makefile.in passes the TCL_LIBRARY value determined by configure through to the Makefile: TCL_LIBRARY = @TCL_LIBRARY@ The AC_SUBST macro specifies what shell variables should be substituted in the Makefile.in template. For example: AC_SUBST(TCL_LIBRARY)
Top
Table of Contents
The AC_INIT macro lists a file that is part of the distribution. The name is relative to the configure.in file. Other possibilities include ../generic/tcl.h or src/mylib.h, depending on where the configure.in file is relative to your sources. The AC_INIT macro necessary to support building the package in different directories (e.g., either tcl8.2/unix or tcl8.2/unix/solaris). The next thing in configure.in is a set of variable assignments that define the package's name and version number: PACKAGE = exampleA MAJOR_VERSION = 0 MINOR_VERSION = 2 PATCH_LEVEL = The package name determines the file names used for the directory and the binary library file created by the Makefile. This name is also used in several configure and Makefile variables. You will need to change all references to "exampleA" to match the name you choose for your package. The version and patch level support a three-level scheme, but you can leave the patch level empty for two-level versions like 0.2. If you do specify a patch-level, you need to include a leading "." or "p" in it. These values are combined to create the version number like this: VERSION = ${MAJOR_VERSION}.${MINOR_VERSION}${PATCH_LEVEL} Windows compilers create a special case for shared libraries (i.e., DLLs). When you compile the library itself, you need to declare its functions one way. When you compile code that uses the library, you need to declare its functions another way. This complicates the exampleA.h header file. Happily, the complexity is hidden inside some macros. In configure.in, you simply define a build_Package variable. The sample defines: AC_DEFINE(BUILD_exampleA) This variable is only set when you are building the library itself, and it is only defined when compiling on Windows. We will show later how this is used in exampleA.h to control the definition of the Examplea_Init procedure. The configure.in file has a bunch of magic to determine the name of the shared library file (e.g., packageA02.dll, packageA.0.2.so, packageA.0.2.shlib, etc.). All you need to do is change one macro to match your package name. AC_SUBST(exampleA_LIB_FILE) These should be the only places you need to edit when adapting the sample configure.in to your extension. It is worth noting that the last macro determines which templates are processed by the configure script. The sample generates two files from templates, Makefile and mkIndex.tcl:
AC_OUTPUT([Makefile mkIndex.tcl]) The mkIndex.tcl script is a script that runs pkg_mkIndex to generate the pkgIndex.tcl file. The pkg_mkIndex command is described in Chapter 12. The mkIndex.tcl script is more complex than you might expect because UNIX systems and Windows have different default locations for binary libraries. The goal is to create a pkgIndex.tcl script that gets installed into the arch/lib/package directory, but can find either arch/bin/package.dll or arch/lib/libpackage.so, depending on the system. You may need to edit the mkIndex.tcl.in template, especially if your extension is made of both Tcl scripts and a binary library. Makefile.in The Makefile.in template is converted by the configure script into the Makefile. The sample Makefile.in is well commented so that it is easy to see where to make changes. However, there is some first class trickery done with the Makefile variables that is not worth explaining in detail. (Not in a Tcl book, at least!) There are a few variables with exampleA in their name. In particular, exampleA_LIB_FILE corresponds to a variable name in the configure script. You need to change both files consistently. Some of the lines you need to change are shown below: exampleA_LIB_FILE = @exampleA_LIB_FILE@ lib_BINARIES = $(exampleA_LIB_FILE) $(exampleA_LIB_FILE)_OBJECTS = $(exampleA_OBJECTS) The @varname@ syntax is used to substitute the configure variable with its platform-specific name (e.g., libexamplea.dll or libexample.so). The lib_BINARIES variable names the set of libraries built by the "make binaries" target. The _OBJECT variable is a clever trick to allow a generic library make rule, which appears in the Makefile.in template as @MAKE_LIB@. Towards the end of Makefile.in, there is a rule that uses these variables, and you must change uses of exampleA it to match your package name: $(exampleA_LIB_FILE) : $(exampleA_OBJECTS) -rm -f $(exampleA_LIB_FILE) @MAKE_LIB@ $(RANLIB) $(exampleA_LIB_FILE) You must define the set of source files and the corresponding object files that are part of the library. In the sample, exampleA.c implements the core of the Secure Hash Algorithm, and the tclexampleA.c file implements the Tcl command interface: exampleA_SOURCES = exampleA.c tclexampleA.c SOURCES = $(exampleA_SOURCES) The object file definitions use the OBJEXT variable that is .o for UNIX and .obj for Windows:
exampleA_OBJECTS = exampleA.${OBJEXT}tclexampleA.${OBJEXT} OBJECTS = $(exampleA_OBJECTS) The header files that you want to have installed are assigned to the GENERIC_HDRS variable. The srcdir Make variable is defined during configure to be the name of the directory containing the file named in the AC_INIT macro: GENERIC_HDRS = $(srcdir)/exampleA.h Unfortunately, you must specify explicit rules for each C source file. The VPATH mechanism is not reliable enough to find the correct source files reliably. The configure script uses AC_INIT to locate source files, and you create rules that use the resulting $(srcdir) value. The rules look like this: exampleA.$(OBJEXT) : $(srcdir)/exampleA.c $(COMPILE) -c '@CYGPATH@ $(srcdir)/exampleA.c' -o $@ The sample Makefile includes several standard targets. Even if you decide not to use the sample Makefile.in template, you should still define the targets listed in Table 45-4 to ensure your extension is TEA compliant. Plans for automatic build environments depend on every extension implementing the standard make targets. The targets can be empty, but you should define them so that make will not complain if they are used.
Makes these targets in order: binaries, libraries, doc. Makes executable programs and binary libraries (e.g., DLLs). Makes platform-independent libraries. Generates documentation files. Makes these targets in order: install-binaries, install-libraries, install-doc. Installs programs and binary libraries. Installs script libraries. Installs documentation files. Runs the test suite for the package. Generates makefile dependency rules.
clean distclean
Removes files built during the make process. Removes files built during the configure process.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
/* * Graphic contexts and */ GC textGC; Tk_TimerToken token; char *clock; int numChars; int textWidth; Tcl_Obj *widthPtr; int textHeight; Tcl_Obj *heightPtr; int padX; Tcl_Obj *padXPtr; int padY; Tcl_Obj *padYPtr; int flags; }Clock; /* * Flag bit definitions. */ #define REDRAW_PENDING 0x1 #define GOT_FOCUS 0x2 #define TICKING 0x4
other support. /* /* /* /* /* /* /* /* /* /* /* /* /* Text graphics context */ Periodic callback handle*/ Pointer to the clock string */ length of the text */ in pixels */ The original width string value*/ in pixels */ The original height string value*/ Horizontal padding */ The original padX string value*/ Vertical padding */ The original padY string value */ Flags defined below */
Top
Table of Contents
Tk_SetClass(tkwin, "Clock"); /* * Allocate and initialize the widget record. */ clockPtr = (Clock *) Tcl_Alloc(sizeof(Clock)); clockPtr->tkwin = tkwin; clockPtr->display = Tk_Display(tkwin); clockPtr->interp = interp; clockPtr->borderWidth = 0; clockPtr->highlightWidth = 0; clockPtr->relief = TK_RELIEF_FLAT; clockPtr->background = NULL; clockPtr->foreground = NULL; clockPtr->highlight = NULL; clockPtr->highlightBg = NULL; clockPtr->tkfont = NULL; clockPtr->textGC = None; clockPtr->token = NULL; clockPtr->clock = NULL; clockPtr->format = NULL; clockPtr->numChars = 0; clockPtr->textWidth = 0; clockPtr->textHeight = 0; clockPtr->padX = 0; clockPtr->padY = 0; clockPtr->flags = 0; /* * Register a handler for when the window is * exposed or resized. */ Tk_CreateEventHandler(clockPtr->tkwin, ExposureMask|StructureNotifyMask|FocusChangeMask, ClockEventProc, (ClientData) clockPtr); /* * Create a Tcl command that operates on the widget. */ clockPtr->widgetCmd = Tcl_CreateCommand(interp, Tk_PathName(clockPtr->tkwin), ClockInstanceCmd, (ClientData) clockPtr, (void (*)()) NULL); /* * Parse the command line arguments. */ if (ClockConfigure(interp, clockPtr, argc-2, argv+2, 0) != TCL_OK) { Tk_DestroyWindow(clockPtr->tkwin); return TCL_ERROR; } interp->result = Tk_PathName(clockPtr->tkwin); return TCL_OK; }
The Tcl_Obj version, ClockObjCmd, does some additional work to set up an option table that is used to efficiently parse the command line options to the clock command. The option table is created the first time the clock command is used. The clientData for ClockObjCmd is initially NULL; it is used to store the option table once it is initialized. While ClockCmd uses the clientData to store a reference to the main Tk window, ClockObjCmd uses the Tk_MainWindow procedure to get a reference to the main Tk window. Example 46-4 The ClockObjCmd command procedure. int ClockObjCmd(clientData, interp, objc, objv) ClientData clientData;/* Main window of the app */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj **objv; /* Argument values. */ { Tk_OptionTable optionTable; Clock *clockPtr; Tk_Window tkwin; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); return TCL_ERROR; } optionTable = (Tk_OptionTable) clientData; if (optionTable == NULL) { Tcl_CmdInfo info; char *name; /* * Initialize the option table for this widget the * first time a clock widget is created. The option * table is saved as our client data. */ optionTable = Tk_CreateOptionTable(interp, optionSpecs); name = Tcl_GetString(objv[0]); Tcl_GetCommandInfo(interp, name, &info); info.objClientData = (ClientData) optionTable; Tcl_SetCommandInfo(interp, name, &info); } tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), Tcl_GetString(objv[1]), (char *) NULL); if (tkwin == NULL) { return TCL_ERROR; } /* * Set resource class. */
Tk_SetClass(tkwin, "Clock"); /* * Allocate and initialize the widget record. */ clockPtr = (Clock *) Tcl_Alloc(sizeof(Clock)); clockPtr->tkwin = tkwin; clockPtr->display = Tk_Display(tkwin); clockPtr->interp = interp; clockPtr->optionTable = optionTable; clockPtr->borderWidth = 0; clockPtr->borderWidthPtr = NULL; clockPtr->highlightWidth = 0; clockPtr->highlightWidthPtr = NULL; clockPtr->relief = TK_RELIEF_FLAT; clockPtr->background = NULL; clockPtr->foreground = NULL; clockPtr->highlight = NULL; clockPtr->highlightBg = NULL; clockPtr->tkfont = NULL; clockPtr->textGC = None; clockPtr->token = NULL; clockPtr->clock = NULL; clockPtr->format = NULL; clockPtr->numChars = 0; clockPtr->textWidth = 0; clockPtr->widthPtr = NULL; clockPtr->textHeight = 0; clockPtr->heightPtr = NULL; clockPtr->padX = 0; clockPtr->padXPtr = NULL; clockPtr->padY = 0; clockPtr->padYPtr = NULL; clockPtr->flags = 0; /* * Register a handler for when the window is * exposed or resized. */ Tk_CreateEventHandler(clockPtr->tkwin, ExposureMask|StructureNotifyMask|FocusChangeMask, ClockEventProc, (ClientData) clockPtr); /* * Create a Tcl command that operates on the widget. */ clockPtr->widgetCmd = Tcl_CreateObjCommand(interp, Tk_PathName(clockPtr->tkwin), ClockInstanceObjCmd, (ClientData) clockPtr, (void (*)()) NULL); /* * Parse the command line arguments. */ if ((Tk_InitOptions(interp, (char *)clockPtr, optionTable, tkwin) != TCL_OK) ||
(ClockObjConfigure(interp, clockPtr, objc-2, objv+2, 0) != TCL_OK)) { Tk_DestroyWindow(clockPtr->tkwin); return TCL_ERROR; } Tcl_SetStringObj(Tcl_GetObjResult(interp), Tk_PathName(clockPtr->tkwin), -1); return TCL_OK; }
Top
Table of Contents
return TCL_ERROR; } result = Tk_ConfigureValue(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, argv[2], 0); } else if ((c == 'c') && (strncmp(argv[1], "configure", len) == 0) && (len >= 2)) { if (argc == 2) { /* * Return all configuration information. */ result = Tk_ConfigureInfo(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, (char *) NULL,0); } else if (argc == 3) { /* * Return info about one attribute, like cget. */ result = Tk_ConfigureInfo(interp, clockPtr->tkwin, configSpecs, (char *) clockPtr, argv[2], 0); } else { /* * Change one or more attributes. */ result = ClockConfigure(interp, clockPtr, argc-2, argv+2,TK_CONFIG_ARGV_ONLY); } } else { Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be cget, configure, position, or size", (char *) NULL); return TCL_ERROR; } return result; } Example 46-6 shows the ClockInstanceObjCmd procedure. It uses the Tk_GetIndexFromObj routine to map the first argument to an index, which is then used in a switch statement. It uses the Tk_GetOptionValue and Tk_GetOptionInfo procedures to parse the widget configuration options. Example 46-6 The ClockInstanceObjCmd command procedure. static int ClockInstanceObjCmd(clientData, interp, objc, objv) ClientData clientData;/* A pointer to a Clock struct */ Tcl_Interp *interp; /* The interpreter */ int objc; /* The number of arguments */ Tcl_Obj *objv[]; /* The command line arguments */ { Clock *clockPtr = (Clock *)clientData;
char *commands[] = {"cget", "configure", NULL}; enum command {CLOCK_CGET, CLOCK_CONFIGURE}; int result; Tcl_Obj *objPtr; int index; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); return TCL_ERROR; } result = Tcl_GetIndexFromObj(interp, objv[1], commands, "option", 0, &index); if (result != TCL_OK) { return result; } switch (index) { case CLOCK_CGET: { if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "cget option"); return TCL_ERROR; } objPtr = Tk_GetOptionValue(interp, (char *)clockPtr, clockPtr->optionTable, (objc == 3) ? objv[2] : NULL, clockPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } break; } case CLOCK_CONFIGURE: { if (objc <= 3) { /* * Return one item if the option is given, * or return all configuration information. */ objPtr = Tk_GetOptionInfo(interp, (char *) clockPtr, clockPtr->optionTable, (objc == 3) ? objv[2] : NULL, clockPtr->tkwin); if (objPtr == NULL) { return TCL_ERROR; } else { Tcl_SetObjResult(interp, objPtr); } } else {
/* * Change one or more attributes. */ result = ClockObjConfigure(interp, clockPtr, objc-2, objv+2); } } } return TCL_OK; }
Top
Table of Contents
/* * Tk_ConfigureWidget parses the command line arguments * and looks for defaults in the resource database. */ if (Tk_ConfigureWidget(interp, clockPtr->tkwin, configSpecs, argc, argv, (char *) clockPtr, flags) != TCL_OK) { return TCL_ERROR; } /* * Give the widget a default background so it doesn't get * a random background between the time it is initially * displayed by the X server and we paint it */ Tk_SetWindowBackground(clockPtr->tkwin, Tk_3DBorderColor(clockPtr->background)->pixel); /* * Set up the graphics contexts to display the widget. * The context is used to draw off-screen pixmaps, * so turn off exposure notifications. */ gcValues.background = Tk_3DBorderColor(clockPtr->background)->pixel; gcValues.foreground = clockPtr->foreground->pixel; gcValues.font = Tk_FontId(clockPtr->tkfont); gcValues.graphics_exposures = False; newGC = Tk_GetGC(clockPtr->tkwin, GCBackground|GCForeground|GCFont|GCGraphicsExposures, &gcValues); if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } clockPtr->textGC = newGC; /* * Determine how big the widget wants to be. */ ComputeGeometry(clockPtr); /* * Set up a call to display ourself. */ if ((clockPtr->tkwin != NULL) && Tk_IsMapped(clockPtr->tkwin) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } return TCL_OK; } Example 46-8 shows the ClockObjConfigure procedure. The Tk_SetOptions interface, which is used to set fields in the Clock data structure, has one potential problem. It is possible that some
configuration options are correct, while others cause errors. In this case, ClockObjConfigure backs out the changes, so the whole configuration has no effect. This requires a two-pass approach, with the second pass used to restore the original values. Tk_SetOptions has a feature that lets you classify changes to the widget. The GEOMETRY_MASK and GRAPHICS_MASK are bits defined by the clock widget to divide its attributes into two classes. It changes its graphics context or recomputes its geometry only if an attribute from the appropriate class is changed. Example 46-8 ClockObjConfigure allocates resources for the widget. static int ClockObjConfigure(interp, clockPtr, objc, objv) Tcl_Interp *interp;/* For return values and errors */ Clock *clockPtr; /* The per-instance data structure */ int objc; /* Number of valid entries in argv */ Tcl_Obj *objv[]; /* The command line arguments */ { XGCValues gcValues; GC newGC; Tk_SavedOptions savedOptions; int mask, error, new; Tcl_Obj *errorResult; /* * The first time through this loop we set the * configuration from the command line inputs. The second * pass is used to restore the configuration in case of * errors */ new = (clockPtr->clock == NULL); for (error = 0 ; error <= 1 ; error++) { if (!error) { /* * Tk_SetOptions parses the command arguments * and looks for defaults in the resource * database. */ if (Tk_SetOptions(interp, (char *) clockPtr, clockPtr->optionTable, objc, objv, clockPtr->tkwin, &savedOptions, &mask) != TCL_OK) { continue; } } else { /* * Restore options from saved values */ errorResult = Tcl_GetObjResult(interp); Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); } if (new || (mask & GRAPHICS_MASK)) {
/* * Give the widget a default background so it doesn't * get a random background between the time it is * initially displayed by the system and we paint it */ Tk_SetBackgroundFromBorder(clockPtr->tkwin, clockPtr->background); /* * Set up the graphics contexts to display the widget. * The context is used to draw off-screen pixmaps, * so turn off exposure notifications. */ gcValues.background = Tk_3DBorderColor(clockPtr->background)->pixel; gcValues.foreground = clockPtr->foreground->pixel; gcValues.font = Tk_FontId(clockPtr->tkfont); gcValues.graphics_exposures = False; newGC = Tk_GetGC(clockPtr->tkwin, GCBackground|GCForeground|GCFont|GCGraphicsExposures, &gcValues); if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } clockPtr->textGC = newGC; } /* * Determine how big the widget wants to be. */ if (new || (mask & GEOMETRY_MASK)) { ComputeGeometry(clockPtr); } /* * Set up a call to display ourself. */ if ((clockPtr->tkwin != NULL) && Tk_IsMapped(clockPtr->tkwin) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } } if (!error) { Tk_FreeSavedOptions(&savedOptions); return TCL_OK; } else { Tcl_SetObjResult(interp, errorResult); Tcl_DecrRefCount(errorResult); return TCL_ERROR; } }
Top
Table of Contents
Example 46-10 shows the Tk_ConfigSpec specification of widget attributes for the clock widget. Example 46-10 Configuration specs for the clock widget. static Tk_ConfigSpec configSpecs[] = { {TK_CONFIG_BORDER, "-background", "background", "Background", "light blue", Tk_Offset(Clock, background), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_BORDER, "-background", "background", "Background", "white", Tk_Offset(Clock, background), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth","2", Tk_Offset(Clock, borderWidth), 0}, {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", "ridge", Tk_Offset(Clock, relief), 0}, {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", "black", Tk_Offset(Clock, foreground), 0}, {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, (char *) NULL, 0, 0}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "red", Tk_Offset(Clock, highlight), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "black", Tk_Offset(Clock, highlight),TK_CONFIG_MONO_ONLY}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "light blue", Tk_Offset(Clock, highlightBg), TK_CONFIG_COLOR_ONLY}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "black", Tk_Offset(Clock, highlightBg), TK_CONFIG_MONO_ONLY}, {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness","HighlightThickness", "2", Tk_Offset(Clock, highlightWidth), 0}, {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Clock, padX), 0}, {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", "2", Tk_Offset(Clock, padY), 0}, {TK_CONFIG_STRING, "-format", "format", "Format", "%H:%M:%S", Tk_Offset(Clock, format), 0}, {TK_CONFIG_FONT, "-font", "font", "Font", "Courier 18", Tk_Offset(Clock, tkfont), 0},
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, 0, 0} }; There is an alternative to the Tk_ConfigureWidget interface that understands Tcl_Obj values in the widget data structure. It uses a a similar type, Tk_OptionSpec, and Tk_ConfigureWidget is replaced by the Tk_SetOptions, Tk_GetOptionValue, and Tk_GetOptionInfo procedures. Example 46-11 shows the Tk_OptionSpec type. Example 46-11 The Tk_OptionSpec typedef. typedef struct Tk_OptionSpec { Tk_OptionType type; char *optionName; char *dbName; char *dbClass; char *defValue; int objOffset; int internalOffset; int flags; ClientData clientData; int typeMask; } Tk_OptionSpec; The Tk_OptionSpec has two offsets, one for normal values and one for Tcl_Obj values. You can use the second offset to set Tcl_Obj values directly from the command line configuration. The TK_CONFIG_PIXELS type uses both offsets. The pixel value is stored in an integer, and a Tcl_Obj is used to remember the exact string (e.g., 0.2cm) used to specify the screen distance. Most of the functionality of the specflags field of Tk_ConfigSpec (e.g., TK_CONFIG_MONO_ONLY) has been changed. The flags field accepts only TK_CONFIG_NULL_OK, and the rest of the features use the clientData field instead. For example, the color types uses clientData for their default on monochrome displays. The typeMask supports a general notion of grouping option values into sets. For example, the clock widget marks attributes that affect geometry and color into different sets. This lets the widget optimize its configuration procedure. Example 46-12 shows the Tk_OptionSpec specification of the clock widget attributes. Example 46-12 The Tk_OptionSpec structure for the clock widget. #define GEOMETRY_MASK 0X1 #define GRAPHICS_MASK 0X2 static Tk_OptionSpec optionSpecs[] = { {TK_OPTION_BORDER, "-background", "background", "Background", "light blue", -1, Tk_Offset(Clock, background), 0, (ClientData) "white", GRAPHICS_MASK}, {TK_OPTION_SYNONYM, "-bg", "background", (char *) NULL,
(char *) NULL, -1, 0,0, (ClientData)"-background", 0}, {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", "2", Tk_Offset(Clock, borderWidthPtr), Tk_Offset(Clock, borderWidth), 0, 0, GEOMETRY_MASK}, {TK_OPTION_SYNONYM, "-bd", "borderWidth", (char *) NULL, (char *) NULL, -1,0,0,(ClientData)"-borderwidth", 0}, {TK_OPTION_RELIEF, "-relief", "relief", "Relief", "ridge", -1, Tk_Offset(Clock, relief), 0, 0, 0}, {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", "black",-1, Tk_Offset(Clock, foreground), 0, (ClientData) "black", GRAPHICS_MASK}, {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL, (char *) NULL, -1, 0,0,(ClientData) "-foreground", 0}, {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "red",-1, Tk_Offset(Clock, highlight), 0, (ClientData) "black", GRAPHICS_MASK}, {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "light blue",-1, Tk_Offset(Clock, highlightBg), 0, (ClientData) "white", GRAPHICS_MASK}, {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness","HighlightThickness", "2", Tk_Offset(Clock, highlightWidthPtr), Tk_Offset(Clock, highlightWidth), 0, 0, GEOMETRY_MASK}, {TK_OPTION_PIXELS, "-padx", "padX", "Pad", "2", Tk_Offset(Clock, padXPtr), Tk_Offset(Clock, padX), 0, 0, GEOMETRY_MASK}, {TK_OPTION_PIXELS, "-pady", "padY", "Pad", "2", Tk_Offset(Clock, padYPtr), Tk_Offset(Clock, padY), 0, 0, GEOMETRY_MASK}, {TK_OPTION_STRING, "-format", "format", "Format", "%H:%M:%S",-1, Tk_Offset(Clock, format), 0, 0, GEOMETRY_MASK}, {TK_OPTION_FONT, "-font", "font", "Font", "Courier 18", -1, Tk_Offset(Clock, tkfont), 0, 0, (GRAPHICS_MASK|GEOMETRY_MASK)}, {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, -1, 0, 0, 0, 0} }; Table 46-1 lists the correspondence between the configuration type of the option and the type of the associated field in the widget data structure. The same types are supported by the Tk_ConfigSpec and Tk_OptionSpec types, with a few exceptions. The TK_CONFIG_ACTIVE_CURSOR configuration type corresponds to the TK_OPTION_CURSOR; both of these set the widgets cursor. The TK_CONFIG_MM and TK_CONFIG_CURSOR types are simply not supported by Tk_OptionSpec because they were not very useful. The TK_OPTION_STRING_TABLE replaces TK_CONFIG_CAP_STYLE and TK_CONFIG_JOIN_STYLE with a more general type that works with Tcl_GetIndexFromObj. In this case, the clientData is an array of strings that are passed to Tcl_GetIndexFromObj. The index value corresponds to the integer
TK_CONFIG_MM TK_CONFIG_PIXELS TK_OPTION_PIXELS TK_CONFIG_RELIEF TK_OPTION_RELIEF TK_CONFIG_STRING TK_OPTION_STRING TK_OPTION_STRING_TABLE TK_CONFIG_SYNONYM TK_OPTION_SYNONYM TK_CONFIG_UID TK_CONFIG_WINDOW TK_OPTION_WINDOW
char *
Top
Table of Contents
Tk_MeasureChars(clockPtr->tkfont, clock, clockPtr->numChars, 0, 0, &clockPtr->textWidth); width = clockPtr->textWidth + 2*(bd + clockPtr->padX); Tk_GeometryRequest(clockPtr->tkwin, width, height); Tk_SetInternalBorder(clockPtr->tkwin, bd); } Finally, we get to the actual display of the widget! The routine is careful to check that the widget still exists and is mapped. This is important because the redisplay is scheduled asynchronously. The current time is converted to a string. This uses the POSIX library procedures gettimeofday, localtime, and strftime. There might be different routines on your system. The string is painted into a pixmap, which is a drawable region of memory that is off-screen. After the whole display has been painted, the pixmap is copied into on-screen memory to avoid flickering as the image is cleared and repainted. The text is painted first, then the borders. This ensures that the borders overwrite the text if the widget has not been allocated enough room by the geometry manager. This example allocates and frees the off-screen pixmap for each redisplay. This is the standard idiom for Tk widgets. They temporarily allocate the off-screen pixmap each time they redisplay. In the case of a clock that updates every second, it might be reasonable to permanently allocate the pixmap and store its pointer in the Clock data structure. Make sure to reallocate the pixmap if the size changes. After the display is finished, another call to the display routine is scheduled to happen in one second. If you were to embellish this widget, you might want to make the uptime period a parameter. The TICKING flag is used to note that the timer callback is scheduled. It is checked when the widget is destroyed so that the callback can be canceled. Example 46-14 shows ClockDisplay. This procedure is identical in both versions of the widget. Example 46-14 The ClockDisplay procedure. static void ClockDisplay(ClientData clientData) { Clock *clockPtr = (Clock *)clientData; Tk_Window tkwin = clockPtr->tkwin; GC gc; /* Graphics Context for highlight */ Tk_TextLayout layout; /* Text measurement state */ Pixmap pixmap; /* Temporary drawing area */ int offset, x, y; /* Coordinates */ int width, height; /* Size */ struct tm *tmPtr; /* Time info split into fields */ struct timeval tv; /* BSD-style time value */ /* * Make sure the clock still exists * and is mapped onto the display before painting. */ clockPtr->flags &= ~(REDRAW_PENDING|TICKING);
if ((clockPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { return; } /* * Format the time into a string. * localtime chops up the time into fields. * strftime formats the fields into a string. */ gettimeofday(&tv, NULL); tmPtr = localtime(&tv.tv_sec); strftime(clockPtr->clock, clockPtr->numChars+1, clockPtr->format, tmPtr); /* * To avoid flicker when the display is updated, the new * image is painted in an offscreen pixmap and then * copied onto the display in one operation. Allocate the * pixmap and paint its background. */ pixmap = Tk_GetPixmap(clockPtr->display, Tk_WindowId(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); Tk_Fill3DRectangle(tkwin, pixmap, clockPtr->background, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); /* * Paint the text first. */ layout = Tk_ComputeTextLayout(clockPtr->tkfont, clockPtr->clock, clockPtr->numChars, 0, TK_JUSTIFY_CENTER, 0, &width, &height); x = (Tk_Width(tkwin) - width)/2; y = (Tk_Height(tkwin) - height)/2; Tk_DrawTextLayout(clockPtr->display, pixmap, clockPtr->textGC, layout, x, y, 0, -1); /* * Display the borders, so they overwrite any of the * text that extends to the edge of the display. */ if (clockPtr->relief != TK_RELIEF_FLAT) { Tk_Draw3DRectangle(tkwin, pixmap, clockPtr->background, clockPtr->highlightWidth, clockPtr->highlightWidth, Tk_Width(tkwin) - 2*clockPtr->highlightWidth, Tk_Height(tkwin) - 2*clockPtr->highlightWidth, clockPtr->borderWidth, clockPtr->relief); } if (clockPtr->highlightWidth != 0) { GC gc;
/* * This GC is associated with the color, and Tk caches * the GC until the color is freed. Hence no freeGC. */ if (clockPtr->flags & GOT_FOCUS) { gc = Tk_GCForColor(clockPtr->highlight, pixmap); } else { gc = Tk_GCForColor(clockPtr->highlightBg, pixmap); } Tk_DrawFocusHighlight(tkwin, gc, clockPtr->highlightWidth, pixmap); } /* * Copy the information from the off-screen pixmap onto * the screen, then delete the pixmap. */ XCopyArea(clockPtr->display, pixmap, Tk_WindowId(tkwin), clockPtr->textGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0); Tk_FreePixmap(clockPtr->display, pixmap); /* * Queue another call to ourselves. The rate at which * this is done could be optimized. */ clockPtr->token = Tk_CreateTimerHandler(1000, ClockDisplay, (ClientData)clockPtr); clockPtr->flags |= TICKING; }
Top
Table of Contents
/* * This results in a call to ClockDestroy. */ Tk_EventuallyFree((ClientData) clockPtr, ClockDestroy); } else if (eventPtr->type == FocusIn) { if (eventPtr->xfocus.detail != NotifyPointer) { clockPtr->flags |= GOT_FOCUS; if (clockPtr->highlightWidth > 0) { goto redraw; } } } else if (eventPtr->type == FocusOut) { if (eventPtr->xfocus.detail != NotifyPointer) { clockPtr->flags &= ~GOT_FOCUS; if (clockPtr->highlightWidth > 0) { goto redraw; } } } return; redraw: if ((clockPtr->tkwin != NULL) && !(clockPtr->flags & REDRAW_PENDING)) { Tk_DoWhenIdle(ClockDisplay, (ClientData) clockPtr); clockPtr->flags |= REDRAW_PENDING; } }
Top
Table of Contents
Final Cleanup
When a widget is destroyed, you need to free up any resources it has allocated. The resources associated with attributes are cleaned up by Tk_FreeOptions. The others you must take care of yourself. The ClockDestroy procedure is called as a result of the Tk_EventuallyFree call in the ClockEventProc. The Tk_EventuallyFree procedure is part of a protocol that is needed for widgets that might get deleted when in the middle of processing. Typically the Tk_Preserve and Tk_Release procedures are called at the beginning and end of the widget instance command to mark the widget as being in use. Tk_EventuallyFree will wait until Tk_Release is called before calling the cleanup procedure. The next example shows ClockDestroy: Example 46-16 The ClockDestroy cleanup procedure. static void ClockDestroy(clientData) ClientData clientData; /* Info about entry widget. */ { register Clock *clockPtr = (Clock *) clientData; /* * Free up all the stuff that requires special handling, * then let Tk_FreeOptions handle resources associated * with the widget attributes. */ if (clockPtr->textGC != None) { Tk_FreeGC(clockPtr->display, clockPtr->textGC); } if (clockPtr->clock != NULL) { Tcl_Free(clockPtr->clock); } if (clockPtr->flags & TICKING) { Tk_DeleteTimerHandler(clockPtr->token); }
if (clockPtr->flags & REDRAW_PENDING) { Tk_CancelIdleCall(ClockDisplay, (ClientData) clockPtr); } /* * This frees up colors and fonts and any allocated * storage associated with the widget attributes. */ Tk_FreeOptions(configSpecs, (char *) clockPtr, clockPtr->display, 0); Tcl_Free((char *) clockPtr); } The version of ClockDestroy that uses the Tcl_Obj interfaces calls Tk_FreeConfigOptions instead of Tk_FreeOptions. The ClockObjDelete command is called when the oclock command is removed from the interpreter. This has to clean up the option table used to parse options, if it has been initialized. There is no corresponding delete procedure for the string-based version of the widget. Example 46-17 shows ClockObjDelete. Example 46-17 The ClockObjDelete command. void ClockObjDelete(ClientData clientData) { Tk_OptionTable optionTable = (Tk_OptionTable) clientData; if (optionTable != NULL) { Tk_DeleteOptionTable(optionTable); } }
Top
Table of Contents
Top
Table of Contents
determine the absolute file name of the program being run. Once this has been done, Tcl_GetNameOfExecutable can be used to get the cached value of the program name.
Memory Allocation
The Tcl_Alloc, Tcl_Realloc, and Tcl_Free procedures provide platform and compiler independent functions to allocation and free heap storage. Use these instead of alloc, realloc, and free. The Tcl_Preserve and Tcl_Release procedures work in concert with Tcl_EventuallyFree to guard data structures against premature deallocation. These are described on page 627.
Lists
You can chop a list up into its elements with Tcl_SplitList, which returns an array of strings. You can create a list out of an array of strings with Tcl_Merge. This behaves like the list command in that it will add syntax to the strings so that the list structure has one element for each of the strings. The Tcl_ScanElement and Tcl_ConvertElement procedures are used by Tcl_Merge. The object interface to lists is provided by Tcl_NewListObj, Tcl_SetListObj, Tcl_ListObjIndex, Tcl_ListObjAppendList, Tcl_ListObjAppendElement, Tcl_ListObjGetElements , Tcl_ListObjLength , and Tcl_ListObjReplace.
Command Parsing
If you are reading commands, you can test for a complete command with Tcl_CommandComplete. You can do backslash substitutions with Tcl_Backslash. A more formal Tcl parser is provided by these procedures: Tcl_ParseCommand, Tcl_ParseExpr, Tcl_ParseBraces, Tcl_ParseQuotedString, Tcl_ParseVarName, and Tcl_FreeParse. The result of the parse is a sequence of tokens, which you can evaluate with Tcl_EvalTokens.
Command Pipelines
The Tcl_OpenCommandChannel procedure does all the work of setting up a pipeline between processes. It handles file redirection and implements all the syntax supported by the exec and open commands. If the command pipeline is run in the background, then a list of process identifiers is returned. You can detach these processes with Tcl_DetachPids, and you can clean up after them with Tcl_ReapDetachedProcs.
If you are implementing an interactive command interpreter and want to use the history facility, then call Tcl_RecordAndEval or Tcl_RecordAndEval. This records the command on the history list and then behaves like Tcl_GlobalEval. You can set the recursion limit of the interpreter with Tcl_SetRecursionLimit. If you are implementing a new control structure, you may need to use the Tcl_AllowExceptions procedure. This makes it acceptable for Tcl_Eval and friends to return something other than TCL_OK and TCL_ERROR. If you want to evaluate a Tcl command without modifying the current interpreter result and error information, use Tcl_SaveResult, Tcl_RestoreResult, and Tcl_DiscardResult.
Evaluating Expressions
The Tcl expression evaluator is available through the Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBoolean, and Tcl_ExprString procedures. These all use the same evaluator, but they differ in how they return their result. The object interface to expressions is implemented with Tcl_ExprLongObj, Tcl_ExprDoubleObj , Tcl_ExprBooleanObj, and Tcl_ExprObj. You can register the implementation of new math functions by using the Tcl_CreateMathFunc procedure.
Converting Numbers
You can convert strings into numbers with the Tcl_GetInt, Tcl_GetDouble, and Tcl_GetBoolean procedures. The Tcl_PrintDouble procedure converts a floating point number to a string. Tcl uses it anytime it must do this conversion.
Tcl Objects
Tcl 8.0 uses dual-ported objects instead of strings to improve execution efficiency. The basic interface to objects is provided by Tcl_NewObj, Tl_DuplicateObj, Tcl_IncrRefCount, Tcl_DecrRefCount, and Tcl_IsShared. Example 44-5 on page 618 and Example 44-15 on page 636 illustrate some of these procedures. You can define new object types. The interface consists of Tcl_RegisterObjType, Tcl_GetObjType, Tcl_AppendAllObjTypes, and Tcl_ConvertToType .
Dynamic Strings
The Tcl dynamic string package is designed for strings that get built up incrementally. You will need to use dynamic strings if you use the Tcl_TranslateFileName procedure. The procedures in the package are Tcl_DStringInit, Tcl_DStringAppend, Tcl_DStringAppendElement, Tcl_DStringStartSublist, Tcl_DStringEndSublist, Tcl_DStringLength , Tcl_DStringValue, Tcl_DStringSetLength, Tcl_DStringFree, Tcl_DString-Result, and Tcl_DStringGetResult. Dynamic strings are explained in more detail on page 628.
There are three sets of procedures that translate strings between encodings. The easiest to use are Tcl_ExternalToUtfDString and Tcl_UtfToExternalDString, which put the result into a Tcl_DString. These are built on top of Tcl_ExternalToUtf and Tcl_UtfToExternal , which are harder to use because they have to deal with partial conversions at the end of the buffer. The Tcl_WinTCharToUtf and Tcl_WinUtfToTChar procedures are for use with Windows TChar type, which is an 8-bit character on Windows 98 and a 16-bit Unicode character on Windows N/T. There are many utility procedures for operating on Unicode and UTF-8 strings: Tcl_UniChar, Tcl_UniCharToUtf, Tcl_UtfToUniChar, Tcl_UniCharToUtfDString, Tcl_UtfToUniCharDString, Tcl_UniCharLen, Tcl_UniCharNcmp, Tcl_UtfCharComplete, Tcl_NumUtfChars, Tcl_UtfFindFirst, Tcl_UtfFindLast, Tcl_UtfNext, Tcl_UtfPrev, Tcl_UniCharAtIndex, Tcl_UtfAtIndex, and Tcl_UtfBackslash. These procedures convert Unicode characters to different cases: Tcl_UniCharToUpper, Tcl_UniCharToLower, and Tcl_UniCharToTitle. These procedures convert strings: Tcl_UtfToUpper, Tcl_UtfToLower, Tcl_UtfToTitle.
Hash Tables
Tcl has a nice hash table package that automatically grows the hash table data structures as more elements are added to the table. Because everything is a string, you may need to set up a hash table that maps from a string-valued key to an internal data structure. The procedures in the package are Tcl_InitHashTable , Tcl_DeleteHashTable, Tcl_CreateHashEntry, Tcl_Delete-HashEntry, Tcl_FindHashEntry , Tcl_GetHashValue, Tcl_SetHashValue, Tcl_GetHashKey, Tcl_FirstHashEntry, Tcl_NextHashEntry , and Tcl_HashStats. Hash tables are used in the blob command example presented in Chapter 44.
Option Processing
The Tcl_GetIndexFromObj provides a way to look up keywords in a table. It returns the index of the
table entry that matches a keyword. It is designed to work with options on a Tcl command. It is illustrated in Example 44-8 on page 622.
File Handlers
Use Tcl_CreateFileHandler to register handlers for I/O streams. You set up the handlers to be called when the I/O stream is ready for reading or writing, or both. File handlers are called after window event handlers. Use Tcl_DeleteFileHandler to remove the handler. is UNIX specific because UNIX has a unified handle for files, sockets, pipes, and devices. On Windows and the Macintosh there are different system APIs to wait for events from these different classes of I/O objects. These differences are hidden by the channel drivers for sockets and pipes. For non-standard devices, the best thing to do is create a channel driver and event source for them.
Tcl_CreateFileHandler
Timer Events
Register a callback to occur at some time in the future with Tcl_CreateTimerHandler. The handler is called only once. If you need to delete the handler before it gets called, use Tcl_DeleteTimerHandler.
Idle Callbacks
If there are no outstanding events, the Tk makes idle callbacks before waiting for new events to arrive. In general, Tk widgets queue their display routines to be called at idle time. Use Tcl_DoWhenIdle to queue an idle callback, and use Tcl_CancelIdleCall to remove the callback from the queue. The Tcl_Sleep procedure delays execution for a specified number of milliseconds.
Input/Output
The Tcl I/O subsystem provides buffering and works with the event loop to provide event-driven I/O. The interface consists of Tcl_OpenFileChannel, Tcl_OpenCommandChannel, Tcl_MakeFileChannel, Tcl_GetOpenFile, Tcl_RegisterChannel, Tcl_UnregisterChannel, Tcl_Close, Tcl_Read, Tcl_ReadChars, Tcl_Gets, Tcl_Write, Tcl_WriteObj , Tcl_WriteChars, Tcl_Flush, Tcl_Seek, Tcl_Tell, Tcl_Eof , Tcl_GetsObj, Tcl_InputBlocked, Tcl_InputBuffered , Tcl_GetChannelOption, and Tcl_SetChannelOption.
name to native syntax. It also expands (~) in file names into user home directories.
Thread Support
The Tcl library is thread safe. It uses the following procedures to serialize access to its data structures: Tcl_MutexLock, Tcl_MutexUnlock, Tcl_ConditionWait , and Tcl_ConditionNotify. Thread local storage is provided by Tcl_GetThreadData. All of these procedures are self-initializing so there are no explicit initialization calls. The Tcl_CreateThreadExitHandler procedure registers a procedure that is called when a thread is terminated. In particular, it can clean up thread local storage. Use Tcl_DeleteThreadExitHandler to remove a registration.
Tcl_FinalizeThread is called when a thread is exiting to clean up state. Currently the thread creation API is still private (TclpCreateThread), and script-level access to threads is provided by an extension.
Exit Handlers
The Tcl_Exit procedure terminates the application. The Tcl_Finalize procedure cleans up Tcl's memory usage and calls exit handlers, but it does not exit. This is necessary when unloading the Tcl DLL. The Tcl_CreateExitHandler and Tcl_DeleteExitHandler set up callbacks that occur when Tcl_Exit is called.
Top
Table of Contents
Creating Windows
The Tk_Init procedure creates the main window for your application. It is described in the TkInit man page. The Tk_CreateWindow and Tk_CreateWindowFromPath are used to create windows for widgets. The actual creation of the window is delayed until an idle point. You can force the window to be created with Tk_MakeWindowExist or destroy a window with Tk_DestroyWindow. The Tk_MainWindow procedure returns the handle on the application's main window. The Tk_MapWindow and Tk_UnmapWindow are used to display and withdraw a window, respectively. They are described in the MapWindow man page. The Tk_MoveToplevelWindow call is used to position a toplevel window. Translate between window names and the Tk_Window type with Tk_Name, Tk_PathName, and Tk_NameToWindow. You can convert from an operating system window ID to the corresponding Tk_Window with Tk_IdToWindow procedure.
Configuring Windows
The configuration of a window includes its width, height, cursor, and so on. Tk provides a set of routines that configure a window and also cache the results. This makes it efficient to query these settings because the system does not need to be contacted. The window configuration routines are Tk_ConfigureWindow, Tk_ResizeWindow, Tk_MoveResizeWindow, Tk_SetWindowBorderWidth, Tk_DefineCursor, Tk_ChangeWindowAttributes, Tk_SetWindowBackground , Tk_SetWindowColormap, Tk_UndefineCursor , Tk_SetWindowBackgroundPixmap, Tk_SetWindowBorderPixmap, Tk_MoveWindow, and Tk_SetWindowBorder . The Tk_SetOptions, Tk_GetOptionValue, Tk_GetOptionInfo, Tk_RestoreSavedOptions, Tk_FreeSavedOptions, Tk_InitOptions, Tk_FreeConfigOptions, and Tk_DeleteOptionTable procedures are used to parse command line options with procedures that use Tcl_Obj values. Canvas item types are supported by Tk_CanvasTagsOption.
Window Coordinates
The coordinates of a widget relative to the root window (the main screen) are returned by Tk_GetRootCoords. The Tk_GetVRootGeometry procedure returns the size and position of a window relative to the virtual root window. The Tk_CoordsToWindow procedure locates the window under a given coordinate.
Window Information
Tk keeps lots of information associated with each window, or widget. The following calls are fast macros that return the information without calling the X server: Tk_WindowId, Tk_Parent, Tk_Display, Tk_DisplayName, Tk_ScreenNumber, Tk_Screen, Tk_X, Tk_Y, Tk_Width, Tk_Height, Tk_Changes, Tk_Attributes, Tk_IsMapped, Tk_IsTopLevel, Tk_ReqWidth, Tk_ReqHeight , Tk_InternalBorderWidth , Tk_Visual, Tk_Depth, and Tk_Colormap.
Register a handler for selection requests with Tk_CreateSelHandler. Unregister the handler with Tk_DeleteSelHandler. Claim ownership of the selection with Tk_OwnSelection. Manipulate the clipboard with Tk_ClipboardClear and Tk_ClipboardAppend.
Event Bindings
The routines that manage bindings are exported by the Tk library so you can manage bindings yourself. For example, the canvas widget does this to implement bindings on canvas items. The procedures are Tk_CreateBindingTable, Tk_DeleteBindingTable, Tk_CreateBinding, Tk_DeleteBinding, Tk_BindEvent , Tk_GetBinding, Tk_GetAllBindings , and Tk_DeleteAllBindings. These are described in the BindTable man page.
Managing Bitmaps
Tk maintains a registry of bitmaps by name, (e.g., gray50 and questhead). You can define new bitmaps with Tk_DefineBitmap, and you can get a handle on the bitmap from its name with Tk_GetBitmap . Related procedures include Tk_NameOfBitmap, Tk_SizeOfBitmap, Tk_GetBitmapFromData, Tk_FreeBitmap, Tk_AllocBitmapFromObj, Tk_GetBitmapFromObj, and Tk_FreeBitmapFromObj.
Geometry Management
A widget requests a certain size with the Tk_GeometryRequest procedure. If it draws a border inside that area, it calls Tk_SetInternalBorder. The geometry manager responds to these requests, although the widget may get a different size. The Tk_ManageGeometry procedure sets up the relationship between the geometry manager and a widget. The Tk_MaintainGeometry procedure arranges for one window to stay at a fixed position relative to another widget. This is used by the place geometry manager. The relationship is broken with the Tk_UnmaintainGeometry call. The Tk_SetGrid call enables gridded geometry management. The grid is turned off with Tk_UnsetGrid.
3D Borders
The three-dimensional relief used for widget borders is supported by a collection of routines described by the 3DBorder man page. The routines are Tk_Get3DBorder, Tk_3DBorderGC, Tk_Draw3DRectangle, Tk_Fill3DRectangle, Tk_Draw3DPolygon, Tk_Free3DBorder, Tk_Fill3DPolygon, Tk_3DVerticalBevel, Tk_3DHorizontalBevel, Tk_SetBackgroundFromBorder, Tk_NameOf3DBorder , Tk_3DBorderColor, Tk_Alloc3DBorderFromObj, Tk_Get3DBorderFromObj, and Tk_Free3DBorderFromObj. Widgets use Tk_DrawFocusHighlight to draw their focus highlight.
Mouse Cursors
Allocate a cursor with Tk_GetCursor, Tk_GetCursorFromData, Tk_GetCursorFromObj, and Tk_AllocCursorFromObj. Map back to the name of the cursor with Tk_NameOfCursor. Release the cursor resource with Tk_FreeCursor or Tk_FreeCursorFromObj.
Graphics Contexts
A graphics context records information about colors, fonts, line drawing styles, and so on. Instead of specifying this information on every graphics operation, a graphics context is created first. Individual graphics operations specify a particular graphic context. Allocate a graphics context with Tk_GetGC and free it with Tk_FreeGC.
Allocate a Pixmap
A pixmap is a simple color image. Allocate and free pixmaps with Tk_GetPixmap and Tk_FreePixmap.
Screen Measurements
Translate between strings like 4c or 72p and screen distances with Tk_GetPixels, Tk_GetPixelsFromObj, Tk_GetMMFromObj, and Tk_GetScreenMM. The first call returns pixels (integers), the second returns millimeters as a floating point number.
Relief Style
Window frames are drawn with a particular 3D relief such as raised, sunken, or grooved. Translate between relief styles and names with Tk_GetRelief, Tk_GetReliefFromObj, and Tk_NameOfRelief.
Atoms
An atom is an integer that references a string that has been registered with the system. Tk maintains a cache of the atom registry to avoid contacting the system when atoms are used. Use Tk_InternAtom to install an atom in the registry, and Tk_GetAtomName to return the name given an atom.
X Resource ID Management
Each window system resource like a color or pixmap has a resource ID associated with it. The Tk_FreeXId call releases an ID so it can be reused. This is used, for example, by routines like Tk_FreeColor and Tk_FreePixmap.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
wish
The wish shell no longer requires a -file (or -f) argument, so you can drop this from your script header lines. This flag is still valid, but no longer necessary. The class name of the application is set from the name of the script file instead of always being Tk. If the script is /usr/local/bin/foobar, then the class is set to Foobar, for example.
Top
Table of Contents
Obsolete Features
Several features that were replaced in previous versions are now completely unsupported. The variable that contains the version number is tk_version. The ancient (version 1) tkVersion is no longer supported. Button widgets no longer have activate and deactivate operations. Instead, configure their state attribute. Menus no longer have enable and disable operations. Instead, configure their state attribute.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Bindings
The hierarchy of bindings has been fixed so that it is actually useful to define bindings at each of the global (i.e., all), class, and instance levels. The new bindtags command defines the order among these sources of binding information. You can also introduce new binding classes, (e.g., InsertMode) and bind things to that class. Use the bindtags command to insert this class into the binding hierarchy. The order of binding classes in the bindtags command determines the order in which bindings are triggered. Use break in a binding command to stop the progression, or use continue to go on to the next level. bindtags $w [list all Text InsertMode $w] The various Request events have gone away: CirculateRequest, ConfigureRequest, MapRequest, and ResizeRequest. The Keymap event is gone, too. Extra modifier keys are ignored when matching events. While you can still use the Any wild card modifier, it is no longer necessary. The Alt and Meta modifiers are set up in a general way so they are associated with the Alt_L, Alt_R, Meta_L, and Meta_R keysyms. Chapter 26 describes bindings starting at page 369.
Top
Table of Contents
Scrollbar Interface
The interface between scrollbars and the scrollable widgets has changed. Happily, the change is transparent to most scripts. If you hook your scrollbars to widgets in the straightforward way, the new interface is compatible. If you use the xview and yview widget commands directly, however, you might need to modify your code. The old interface still works, but there are new features of these operations that give you even better control. You can also query the view state so you do not need to watch the scroll set commands to keep track of what is going on. Finally, scrollable widgets are constrained so that the end of their data remains stuck at the bottom (right) of their display. In most cases, nothing is broken by this change. Chapter 30 describes the scrollbar protocol starting at page 431.
Top
Table of Contents
pack info
Version 3 of Tk introduced a new syntax for the pack command, but the old syntax was still supported. This continues to be true in nearly all cases except the pack info command. If you are still using the old packer format, you should probably take this opportunity to convert to the new packer syntax. The problem with pack info is that its semantics changed. The new operation used to be known as pack newinfo . In the old packer, pack info returned a list of all the slaves of a window and their packing configuration. Now pack info returns the packing configuration for a particular slave. You must first use the pack slaves command to get the list of all the slaves and then use the (new) pack info to get their configuration information. Chapter 23 describes the pack geometery manager starting at page 336.
Top
Table of Contents
Focus
The focus mechanism has been cleaned up to support different focus windows on different screens. The focus command takes a -displayof argument. Tk remembers which widget inside each toplevel has the focus. When the focus is given to a toplevel by the window manager, Tk automatically assigns focus to the right widget. The -lastfor argument queries which widget in a toplevel will get the focus by this means. Chapter 36 describes focus starting at page 518. The focus default and focus none commands are no longer supported. There is no real need for focus default anymore, and focus none can be achieved by passing an empty string to the regular focus command. The tk_focusFollowsMouse procedure changes from the default explicit focus model where a widget must claim the focus to one in which moving the mouse into a widget automatically gives it the focus. The tk_focusNext and tk_focusPrev procedures implement keyboard traversal of the focus among widgets. Most widgets have bindings for <Tab> and <Shift-Tab> that cycle the focus among widgets.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Radiobutton Value
The default value for a radiobutton is no longer the name of the widget. Instead, it is an empty string. Make sure you specify a -value option when setting up your radiobuttons.
Top
Table of Contents
Entry Widget
The scrollCommand attribute changed to xScrollCommand to be consistent with other widgets that scroll horizontally. The view operation changed to the xview operation for the same reason. Chapter 31 describes the entry widget starting on page 437. The delete operation has changed the meaning of the second index so that the second index refers to the character just after the affected text. The selection operations have changed in a similar fashion. The sel.last index refers to the character just after the end of the selection, so deleting from sel.first to sel.last still works. The default bindings have been updated, of course, but if you have custom bindings you must fix them.
Top
Table of Contents
Menus
The menu associated with a menubutton must be a child widget of the menubutton. Similarly, the menu for a cascade menu entry must be a child of the menu. The @y index for a menu always returns a valid index, even if the mouse cursor is outside any entry. In this case, it simply returns the index of the closest entry, instead of none. The selector attribute is now selectColor. The postcascade operation posts the menu of a cascade entry: $menu postcascade index The insert operation adds a menu entry before a specified entry: $menu insert index type options... Chapter 27 describes menus starting at page 396.
Top
Table of Contents
Listboxes
Listboxes changed quite a bit in Tk 4.0. See Chapter 32 for all the details. There are now four Motiflike selection styles, and two of these support disjoint selections. The tk_listboxSingleSelect procedure no longer exists. Instead, configure the selectMode attribute of the listbox. A listbox has an active element, which is drawn with an underline. It is referenced with the active index keyword. The selection commands for listboxes have changed. Change: $listbox select from index1 $listbox select to index2 to: $listbox select anchor index1 $listbox select set anchor index2 The set operation takes two indices, and anchor is a valid index, which typically corresponds to the start of a selection. You can selectively clear the selection, and query if there is a selection in the listbox. The command to clear the selection has changed. It requires one or two indices. Change: $listbox select clear to: $listbox select clear 0 end
Top
Table of Contents
No geometry Attribute
The frame, toplevel, and listbox widgets no longer have a geometry attribute. Use the width and height attributes instead. The geometry attribute got confused with geometry specifications for toplevel windows. The use of width and height is more consistent. Note that for listboxes the width and height are in terms of lines and characters, while for frames and toplevels it is in screen units.
Top
Table of Contents
Text Widget
The tags and marks of the text widgets have been cleaned up a bit, justification and spacing are supported, variable tab stops can be defined, and you can embed widgets in the text display. A mark now has a gravity, either left or right, that determines what happens when characters are inserted at the mark. With right gravity you get the old behavior: the mark gets pushed along by the inserted text by sticking to the right-hand character. With left gravity it remains stuck. The default is right gravity. The mark gravity operation changes it. When text is inserted, it only picks up tags that are present on both sides of the insert point. Previously it would inherit the tags from the character to the left of the insert mark. You can also override this default behavior by supplying tags to the insert operation. The widget scan operation supports horizontal scrolling. Instead of using marks like @y, you need a mark like @x,y. For a description of the new features, see Chapter 33.
Top
Table of Contents
Color Attributes
Table 48-1 lists the names of the color attributes that changed. These attributes are described in more detail in Chapter 38 starting at page 536.
Tk4.0
selectColor Scrollbar.activeBackground troughColor Scrollbar.background Scale.activeBackground troughColor Scale.background highlightBackground highlightColor
Top
Table of Contents
Top
Table of Contents
Canvas scrollincrement
The canvas widget changed the scrollIncrement attribute to a pair of attributes: xScrollIncrement and yScrollIncrement. The default for these is now one-tenth the width (height) of the canvas instead of one pixel. Scrolling by one page scrolls by nine-tenths of the canvas display.
Top
Table of Contents
The Selection
The selection support has been generalized in Tk 4.0 to allow use of other selections such as the CLIPBOARD and SECONDARY selections. The changes do not break anything, but you should check out the new clipboard command. Some other toolkits, notably OpenLook, can only paste data from the clipboard. Chapter 35 describes the selection starting at page 507.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Cross-Platform Scripts
Cross-platform support lets a Tcl/Tk script run unchanged on UNIX, Windows, and Macintosh. However, you could still have platform dependencies in your program. The most obvious is if your script executes other programs or uses C-level extensions. These need to be ported for your script to continue to work.
Newline Translations
Windows and Macintosh have different conventions for representing the end of line in files. These differences are handled automatically by the new I/O subsystem. However, you can use the new fconfigure command described on page 221 to control the translations.
The Windows and Macintosh versions of wish have a built-in console. The commands you enter in the console are evaluated in the main Tcl interpreter, but the console is really implemented in another Tcl interpreter to avoid conflicts. You can show and hide the console with the console command, which is described on page 28.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Network Sockets
The socket command provides access to TCP/IP sockets. There are C APIs to define new channels, and there are extensions that provide UDP and other protocols. Chapter 17 describes sockets starting on page 228. Example 40-4 on page 567 uses sockets as a replacement for the Tk send command. info hostname The info hostname command was added to find out your host identifier.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Virtual Events
The new event command defines virtual events like <<Cut>> <<Copy>> and <<Paste>>. These virtual events map to different physical events on different platforms. For example, <<Copy>> is <Control-c> on Windows and <Command-c> on Macintosh. You can write your scripts in terms of virtual events, and you can define new virtual events for your application. You can also use the event command to generate events for testing purposes. Virtual events and the event command are described starting at page 380.
Top
Table of Contents
Standard Dialogs
Several standard dialogs were added to Tk. These let you display alerts, prompt the user, choose colors, and select files using dialogs that are implemented in native look for each platform. For example, to ask the user a yes/no question: tk_messageBox -type yesno \ -message "Ok to proceed?" \ -icon question => yes To open an existing file: set file [tk_getOpenFile] The standard dialogs are described in Chapter 36 starting at page 515.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Compile-Time Errors
The compiler catches some sorts of errors earlier than the pure interpreted version. The first time a compiler runs a procedure, it translates the whole thing to byte codes first. If there are syntax errors at the end of the procedure it prevents any code in the procedure from running. A similar problem occurs with list data. If a string is not a perfect list, then the list commands will fail when parsing it, even if they do not use the damaged part of the list. For example, lindex used to process only enough of a list to find the requested element. In Tcl 8.0 the whole list is converted into a native representation. Errors at the end of a list will prevent you from getting elements at the beginning. This is mainly an issue when you use list operations on arbitrary input data.
The binary format and binary scan commands support conversions between binary data and strings. These are described on page 54. The unsupported0 command was improved and became the fcopy command, which is described on page 239.
Top
Table of Contents
Namespaces
Chapter 14 describes the Tcl namespace facility that partitions the global scope for variables and procedures. Namespaces are optional. Simple scripts can avoid them, but larger applications can use them for structuring. Library packages should also use namespaces to facility code sharing without conflict.
Top
Table of Contents
Safe-Tcl
Hidden commands were added to the Safe-Tcl security model. Instead of removing unsafe commands from an interpreter, they are hidden. The master can invoke hidden commands inside a slave. This is necessary so that the command sees the correct context. This adds new operations to the interp command: invokehidden, hide, expose, and hidden. Hidden commands are described on page 281. Initialization of a safe interpreter with a safe base that supports auto loading and a standard exit alias has been abstracted into a Tcl interface. The safe::interpCreate and safe::interpInit procedures create or initialize a slave with the safe base. The safe::interpDelete procedure cleans up. The safe base is described on page 284. To support the Trusted security policy, the interp marktrusted command was added. This promotes an unsafe interpreter back into a trusted one. Of course, only the master can do this.
Top
Table of Contents
New lsort
The lsort command was reimplemented. The new implementation is reentrant, which means you can use lsort inside a sorting function called by lsort. New options have lessened the need for custom sorting procedures, too. The -dictionary option sorts cases together and it handles numbers better. The -index option sorts lists on a key field. These are described on page 65.
Top
Table of Contents
tcl_precision
Variable
The tcl_precision variable was removed in the 8.0p2 release, and added back in the 8.0.3 release. Its default value was increased from 6 to 12, which should be enough for most applications.
Top
Table of Contents
Top
Table of Contents
Http Package
A Tcl implementation of the HTTP/1.0 protocol was added to the Tcl script library. The http::geturl command is described on page 240.
Top
Table of Contents
Top
Table of Contents
Platform-Independent Fonts
A platform-independent font naming system was added in Tk 8.0. Names like times 10 bold are interpreted on all platforms. The font command lets you create font objects that can be associated with widgets. The font metrics command returns detailed size information. The font command is described on page 554.
Top
Table of Contents
Top
Table of Contents
Application Embedding
Tk supports application embedding. Frames and toplevels have a -container attribute that indicates they embed another application. This is necessary for geometry management and focus protocols. Frames and toplevels have a -use parameter that embeds them into an existing window. Wish also takes a -use command-line argument. Embedding is described on page 419 and 296.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
grid rowconfigure
The grid columnconfigure and rowconfigure commands take an argument that specifies a row or column. This value can be a list: grid columnconfigure {0 3}-weight 1
Top
Table of Contents
installation or a standard build environment. This supports sites that have several Tcl installations and helps extensions find their correct script library. The usage of tcl_findLibrary is: tcl_findLibrary base version patch script enVar varName The base is the prefix of the script library directory name. The version is the main version number (e.g., "8.0"). The patch is the full patch level (e.g., "8.0.3"). The script is the initialization script to source from the directory. The enVar names an environment variable that can be used to override the default search path. The varName is the name of a variable to set to name of the directory found by tcl_findLibrary. A side effect of tcl_findLibrary is to source the script from the directory. An example call is: tcl_findLibrary tk 8.0 8.0.3 tk.tcl TK_LIBRARY tk_library auto_mkindex_old The auto_mkindex procedure was reimplemented by Michael McLennan to support [incr Tcl] classes and methods. This changed the semantics somewhat so that all procedures are always indexed. Previously, only procedures defined with the word "proc" starting at the beginning of a line were indexed. The new implementation sources code into a safe interpreter and watches for proc commands however they are executed, not how they are typed into the source file. The old version of auto_mkindex was saved as auto_mkindex_old for those applications that used the trick of indenting procedure definitions to hide them from the indexing process.
safe::loadTk This procedure was extended to take a -display displayname argument so you can control where the main window of the safe interpreter is created. Its -use argument was extended to take either window IDs or Tk window pathnames.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Thread Safety
The Tcl C library is thread-safe. This means you can use Tcl in an application that uses threads. The threading model for Tcl is that a thread can have one or more Tcl interpreters, but a Tcl interpreter cannot be used by different threads. For communication between threads, Tcl provides the ability to send Tcl scripts to an interpreter in another thread. You can also create an extension that implements shared data and does its own locking. The Tcl C library provides mutex variables, condition variables, and thread local storage. These primatives are used by Tcl internally, and they are meant to be used by Tcl extensions to serialize access to their own data structures. The Tcl library allows different implementations of the threading primatives. This is done to support Unix, Windows, and Macintosh. Tcl uses native threads on Windows, and Posix pthreads on Unix. MacOS does not have true threads, so it is easy to provide the required thread API.
testthread create ?script? testthread id testthread errorproc proc testthread exit testthread names testthread send id ?-async? script testthread wait
Creates a new thread and a Tcl interpreter. Runs script after creating the Tcl interpreter. If no script is specified, the new thread waits with testthread wait. Returns the thread ID of the current thread. Register proc as a hander for errors from other threads. If they terminate with a Tcl error, this procedure is called with the error message and errorInfo values as arguments. Otherwise a message is printed to stderr. Terminate the current thread. Returns a list of thread IDs. Send a script to another thread for evaluation. If -async is specified, the command does not wait for the result. Enter the event loop. This is used by worker threads to wait for scripts to arrive for evaluation.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Registers the current process as a DDE service with name TclEval and the given topic. If topic is not specified, this command returns the currently registered topic. Sends data to the service with the given topic. Sends cmd and its arguments to the TclEval service with the given topic. This is an alternative to the Tk send command. Similar to the execute operation, but some services export operations under poke instead of execute. Fetches the named item from the service with the given topic. Returns server and topic if that server currently exists, otherwise it returns the empty string. Returns all the topics implemented by server. Returns all servers that implement topic. Returns all server, topic registrations.
Top
Table of Contents
Miscellaneous
Serial Line I/O
The serial line drivers were converted to use threads so you can wait for I/O with fileevent when using serial devices. There is no API change here, but this was a limitation that was annoying in previous Tcl releases. tcl_platform(user) The tcl_platform(user) array element records the currently logged in user. This masks difference in environment variables and system calls used to get this information on different platforms.
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
Top
Table of Contents
generated by regsub. The input data that is not matched by regsub is untouched by the subsequent subst. This can eliminate the need to quote special Tcl characters in input, which takes extra regsub passes. The -command option passes the replacement string to a command before substitution into the result. You can achieve similar effects with both -subst and -command, so we may only implement one option. A -start option will be added to regsub and regexp. This indicates a starting offset into the string being matched.
Internationalization
There are still some improvements that can be made in Internationalization support, especially with respect to input methods.
Top
Table of Contents
Proposed Tk Changes
The Img Patch
The Img patch adds an alpha channel, better transparency support, and improved GIF support, including the ability to save GIF images. This patch also supports other image types (e.g., JPEG) that can be loaded as extensions.
Canvas Improvements
The canvas implementation needs to be converted to use Tcl_Obj values, which will improve its performance. Advanced tag searching may be added. This adds the ability to search for canvas items based on boolean expressions of tag values. Dashed lines will be added, even though we can support only single-pixel wide dashed lines on Windows 95. Windows NT, UNIX, and Macintosh can support thick dashed lines. Canvas coordinates may be specified as a single list argument instead of individual arguments, which makes it easier to construct commands. The "Visitors Patch" may be adopted, so that extensions can add new canvas operations without patching Tk directly.
Pointer Warping
Tk applications will have the ability to move the mouse under program control. Use the wm warp command: wm warp x y
Hidden Text
The TkMan application uses hidden text to suppress text display. This works by adding a new tag property.
Top
Table of Contents
has a nice set of development tools. You can get a demo license for the tools at: http://www.scriptics.com/registration/welchbook.html The exsource folder contains the examples from the book. These are automatically extracted from Framemaker files. One thing to watch out for is that single quotes get extracted as \" . I have left these as-is. The browser.tcl script lets you view and try out the examples. The tclhttpd directory contains an unpacked version of the TclHttpd distribution. You should be able to start the server by loading the bin/httpd.tcl script into wish or tclsh . This script starts a Web server on part 8015. The tea_sample directory contains a sample Tcl extension from the Tcl Extension Architecture. This example illustrates how to create Tcl extensions on Windows and UNIX. The download folder contains software downloaded from the Internet. Some of these have associated .README files. Others are described by files in the index folder. These descriptions are derived from the Tcl Resource Center found at: http://www.scriptics.com/resource/ The index/index.html file provides a listing and description of the software in the download folder. The CD_UTILS folder contains software you may find helpful, such as Winzip and a version of Tar for Macintosh. I have also included the mkhybrid software used to create the CD image, as well as other scripts used in the process. The welch folder contains a picture of me and a copy of my PGP key. The key ID is 7EDF9C79 and its fingerprint is: 94 D8 90 6A FD 9C AE 94 40 E3 C6 D6 B1 90 E0 03
Top
Table of Contents
Technical Support
Prentice Hall does not offer technical support for this software. If there is a problem with the media, however, you may obtain a replacement CD by emailing a description of the problem. Send your email to: disc_exchange@prenhall.com
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z]
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] ( ) for arrays -fill, pack option -ipadx and -ipady pack options -padx and -pady, pack options -sticky, grid option /bin/sh _to run a Tcl script { } for argument grouping ~ in file names 17362 MyExample Example 14-12_ The message widget formats long lines of text. 19809 H2x Tcl and Tk Versions 25346 H2 Event Loop Interface 27139 H2x World Wide Web 3D Border 3D relief sampler 41514 MyExample Example 41-6_ A canonical Tcl main program and Tcl_AppInit.
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] abbreviation of commands AC_INIT AC_OUTPUT AC_SUBST accepting socket connections activate window event activeBorderWidth, widget attribute activeRelief, widget attribute add elements to a list addinput [See fileevent] administration, TclHttpd Advanced Regular Expressions 2nd after, safe alias after, Tcl command aliases, command aliases, introspection on Allocate a Pixmap Allocation, Memory Alternation in regular expressions anchor position, pack Anchor Positions, Text anchoring a regular expression Anchoring text in a label or button animation with images animation with update 2nd 3rd ANSI_ARGS app-defaults file 2nd append, Tcl command AppleScript on Macintosh applets application activation Application and User Resources application deactivation Application Direct URL form handlers specifying content type
application embedding 2nd Application Initialization application name 2nd arc canvas item Architecture, Tcl Extension args, example 2nd args, parameter keyword 2nd Arguments and Tcl_GetIndexFromObj, Parsing Arguments, Main Programs and Command-Line argv, saving for window session argv, Tcl variable 2nd arithmetic on text indices array ArrayInvert collecting variables convert to list created with variable trace empty variable name for a Database for records 2nd global 2nd list of syntax Tcl command arrow keys arrow on canvas aspect ratio, message widget AssocData for per Interpreter Data Structures Associating State with Data asynchronous I/O atom, in C attribute activeBorderWidth activeRelief bitmap borderWidth Button Widgets colormap colors, all configuring in C elementBorderWidth Entry Widget Frames and Toplevels geometry, old image Label Widget relief Scale Widget setgrid size types, in C visual
Auto Loading and auto_import auto loading, description auto_import, procedure hook auto_mkindex_old 2nd auto_noexec, Tcl variable 2nd auto_noload, disable library auto_path, Tcl variable 2nd 3rd autoconf, configure and autoconf, tcl.m4 file autoconf, using automatic program execution 2nd automatic quoting
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Back References background errors 2nd background I/O reader background proceses, in C Backslash Quoting backslash sequences 2nd backslash-newline, in quoted string BackSpace key Ball, Steve balloon help baud rate, I/O channel Beasley, David beep [See bell] bell, Tk command bgerror 2nd 3rd Binary Data and File I/O Binary Data, ByteArrays binary data, pack binary data, unpack binary encoding Binary String Support binary, Tcl command bind, Tk command Bind_Display Bind_Edit Bind_Interface Bind_New Bind_Read Bind_Save BindDefine BindDragto binding adding to arrow keys break and continue button modifiers
canvas object 2nd canvas text objects class command to event continue destroy window different binding tags double click event syntax event types global in C code keyboard events Meta and Escape mouse events order of execution scale widget scrollbar widget sequence of events tab key tag, defining text tags text widget 2nd Tk 4.0 changes top-level windows user interface for window changes size window dragging BindMark BindSelect bindtags, Tk command BindYview bitmap built-in canvas item definition in C code image type in label on canvas widget attribute Blob and BlobState blob Command Example Blob_Init and BlobCleanup procedures BlobCommand and BlobPoke BlobCreate and BlobDelete BlobData and BlobN BlobNames procedure BlobState data structures BLT bold text 2nd book Web site boolean expressions
boolean preference item Borders and padding borders vs. padding, example Borders, 3D borderWidth, widget attribute Borenstein, Nathaniel Bound Quantifiers box on canvas break and continue in bindings break, Tcl command Brouwers, Jean Brower Plugin Compatiblity browse selection mode, listbox browser for the code examples Buffering, I/O Channel Building a List Building Tcl from Source built-in bitmaps bulleted list button associated with a Tcl procedure associated with variables attributes command container for 2nd emulate in text widget fixing a troublesome situation mouse as event modifiers operations padding padding vs. packer padding problems with command row of 2nd scope of command Tk widget user-defined byte code compiler 2nd ByteArrays for Binary Data
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] C command procedure. command procedure C Library, Using the Tcl C Programming and Tcl C Shell History, comparision C, creating commands in C, evaluate Tcl command from C, Invoking Scripts from C, Tcl_Eval runs Tcl commands from call by name 2nd call stack, viewing callbacks 2nd code wrapper idle into a namespace scope for socket accept Calling C command with Tcl_Invoke Calling Out to Tcl Scripts Canv_Tkwin canvas arc object arrow attributes bindings on objects bindings, text object bitmap object bounding box C interface circle coordinate space coordinates vs. screen coordinates coordinates, large copy and paste dashed lines display list
drag object embedded window events coordinates gridded geometry Hello,World! hit detection image object large scroll area line object min max scale moving objects object bindings object support in C objects with many points oval object polygon object postscript proposed improvements rectangle object resources for objects rotation scaling objects 2nd scroll increment scroll region 2nd selection handler example spline stroke drawing example Summary of Operations tag on object 2nd text object attributes text object bindings transparent text Cap Styles, Line capturing program output Capturing Subpatterns Cascaded Menus case [See switch] catalog files, managing message catalog files, sample message catalogs and namespaces, message catalogs, message catch, example 2nd catch, possible return values catch, Tcl command Catching More Than Errors Cavity Model, Pack cd, Tcl command CDE Border Width Centering a Window centimeters cget, widget operation CGI
Application, Guestbook Argument Parsing cgi.tcl package definition of Directories example script script library for cgi.tcl CGI package Cgi_Header Cgi_Parse and Cgi_Value Chained conditional with elseif change directory Changing Command Names with rename changing widgets 2nd Channel Drivers, I/O channels, stacking character code Character Set Conversions character set encoding Character Set Encodings Character Sets 2nd characters from strings checkbutton, Tk widget choice preference item Choosing items from a listbox Choosing the Parent for Packing Chopping File Pathnames circle, canvas object ckalloc and ckfree class, event binding class, of application class, resource class, widget clicks per second client side of remote evaluation Client Sockets 2nd ClientData CLIPBOARD selection 2nd clipboard, Tk command clock widget Clock widget data structure clock, Tcl command Clock_Init procedure ClockCmd command procedure ClockConfigure allocates resources for the widget ClockDestroy cleanup procedure ClockDisplay procedure ClockEventProc handles window events ClockInstanceCmd command procedure ClockInstanceObjCmd command procedure ClockObjCmd command procedure ClockObjConfigure, Tcl_Obj version
ClockObjDelete command close a window close errors from pipes close window callback close, Tcl command Closing I/O channels code checker Code Warrior compiler code, procedure for callbacks Codes from Command Procedures Coding Style Collating Elements color allocating in C attributes darker color dialog to choose name of text palettes resource names reverse video RGB specification Tk 4.0 new attributes values values, in C colormap for frame in C widget attribute command abbreviations aliases for safe interpreters aliases, saving as Tcl commands body build with list buttons C interface, String C interface, Tcl_Obj call with Tcl_Invoke callbacks complete Tcl command creating and deleting defined by a namespace, listing evaluate from C evaluation example, blob for entry widget from C using Tcl_Eval history 2nd implement in C 2nd lookup
on radiobutton or checkbutton parsing in C passing variable names prefix callbacks reading commands substitution syntax that concatenates its arguments that uses regular expressions Command key, Macintosh command line arguments command procedure 2nd and Data Objects BlobCmd call with Tcl_Invoke RandomCmd RandomObjCmd Result Codes from command substitution command-line argument to Wish arguments 2nd 3rd arguments, in C arguments, main programs and parsing comments in line in resource file in switch Communicating Processes Comparing file modify times comparing text indices comparison function, sorting a list Compatibility, regular expression patterns Compile-Time Errors compiler, byte code compiler, Code Warrior compiler, Microsoft Visual C++ compiler, TclPro Compiling Tcl and Extensions complex indices for arrays Computing a darker color concat and eval concat and lists concat, list, double quotes comparison concat, Tcl command concatenate strings and lists conditional, if then else. config/plugin.cfg file configure and autoconf configure flags, Standard
configure, widget operation configure.in 2nd configuring attributes, in C 2nd Read-Write Channels Security Policies widget attributes 2nd window, in C windows Connect client to an eval server connect, socket connection state, TclHttpd console, Tcl command 2nd Constructing Code with the list Command Constructing Lists Constructing Procedures Dynamically containers. [See frame.] content type for Application Direct URL Contexts, Graphics continue in bindings continue, Tcl command Control Structure Commands Conversions Between Encodings Conversions, Character Set Converting Between Arrays and Lists Converting Existing Packages to Namespaces Converting Numbers coordinate space, canvas coordinates of mouse event coordinates, general Coordinates, Window copy and paste Copy, virtual event Copying Files corner grips Correct Quoting with eval counting with regsub Covering a window with place create commands in C directories elements in a hash table file pathnames hash tables in C hierarchy of interpreters interpreter in C interpreter in scripts interpreters interpreters in C loadable package windows in C 2nd creating
image types in C cross-platform cancel event file naming scripts virtual events curly braces stripped off vs. double quotes current directory cursor in C mouse text insert Custom Dialogs Cut, virtual event CVS repository for Tcl software Cygwin tools
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Data Objects, C Command Procedures and Data Structures with Arrays Data Structures, AssocData for per Interpreter data transformation Data, Using Tcl_Preserve and Tcl_Release to Guard data-driven user interface database, ODBC database, simple in-memory 2nd Database, Using the Resource DDE Extension deactivate window event debugger, TclPro debugger, Tuba Debugging declaring variables Decoding HTML Entities default button default parameter values Defining New Binding Tags delete commands in C files interpreters in C list element by value text desktop icons destroy no errors Tk command widget window event Destroying Hash Tables, Creating and detached window dialog buiding with procedure custom
data-driven approach message for simple example to make choice window for dictionary [See array] directory current Disabling the Library Facility display list graphics display space, with pack Display, Fonts and Text distribution, Tcl source code DLL, loading into Tcl 2nd DLLEXPORT document root, TclHttpd document type handler, TclHttpd dollar sign dollar sign syntax Don Libes DOS to UNIX double click Double quotes compared to concat and list double quotes vs. curly braces Doyle, Michael dp_send, Tcl command drag object on canvas drag out a box drag windows, bindings drag-and-drop Drivers, I/O Channel DString interface 2nd Dykstra, Dave Dynamic HTML, CGI dynamic linking 2nd Dynamic Loading and Packages Dynamic Strings, DString
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] e-mail application, exmh Echo Service, example EchoArgs edit bindings for canvas text editable text Effective Tcl/Tk Programming, book elementBorderWidth, widget attribute else [See if] elseif [See if] Emacs-like binding for Meta and Escape embed_args and plugin, variables embedded applications embedded images for a bulleted list embedded images in text embedded widgets in text embedded window in frame embedded window on canvas Embedding Tk Windows Empty Array Names encoding binary character set, in C Character Sets Conversions Between fconfigure option for character sets I/O channels Scripts in Different system Tcl command 2nd End of File Character End of Line Translations end-of-file condition entry attributes changes in Tk 4.1 command
indices long strings in read-only tips for use Tk 4.0 changes variable for value with label Environment Variables eof, Tcl command Equal-sized labels EqualSizedLabels Equivalence Classes errno error background, in Tcl catching errorCode, variable errorInfo, variable 2nd from return handler for Tk information in C reporting script Tcl command X protocol Escape key eval and double quotes example 2nd in aliases server 2nd Tcl command wrapper procedure example Evaluating Expressions from C Evaluating Tcl Commands from C event bindings bindings, in C handler, in C handler, resize in C keywords modifier sequences syntax, bindings Tk command types, binding virtual Event Loop Event Loop Implementation event loop in tclsh Event Loop Interface Event Loop Moves from Tk to Tcl event sources
event, I/O handler Event-Driven Programming Events in C, Timer Events in C, Window Examining the File System Example Browser Example Plugins Example, The blob Command exception [See catch] exec, limitations on Windows exec, Tcl command ExecLog execute programs automatically executing programs Execution Environment Exit Handlers exit, command alias exit, Tcl command exmh, e-mail application expand vs. fill, with pack expand, more than one widget Expanding Tilde in File Names Expanding Variables in Resources Expect Expect, Tcl extension Exploring Expect, book Exporting and Importing Procedures expr, Tcl command Expressions and String Matching, Regular Expressions, Evaluating expressions, from C code extended selection mode, listbox Extension Architecture, Tcl Extension, Compiling Extension, The Sample EXTERN
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] fblocked, Tcl command fconfigure, changes in Tcl 8.0 fconfigure, changes in Tcl 8.1 fconfigure, changes in Tk 4.1 fconfigure, File Encodings and fconfigure, Tcl command fcopy, HttpCopyDone examle fcopy, Tcl command feedback, to user file attributes channel names compare modify times current script dialogs encodings and fconfigure end of line character end-of-file equality test find by name for preferences I/O and Binary Data limited access with safe interp name manipulation name patterns of widget attributes open for I/O operations added in Tcl 7.6 partial re-write Tcl command tilde in names File Handlers File Name Manipulation in C File System, Examining the File_Process fileevent, Tcl command 2nd
fill vs. expand, with pack find file by name find text in widget flush, Tcl command Flynt, Clif focus and dialogs events grab grab, tkwait sequence highlight 2nd introduction tab binding Tk 4.0 changes Tk command tk_focusNext font and text attributes failure to find family or typeface in C code metrics missing platform-independent resource scaling size selection dialog 2nd selection example system Tk command FontWidget handles missing fonts for loop, example for, Tcl command foreach, multiple loop variables foreach, Tcl command form data, TclHttpd form handlers Form, newguest form, self-checking HTML Format Templates format text with message widget format, Tcl command 2nd Forms and Processing Form Data, HTML frame as container 2nd attributes colormap nested for packing packing example positioned on canvas size Tk widget
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] geometry gridding gridding, listbox management in C manager canvas pack 2nd place text old attribute propagation, turning off GET, HTTP protocol gets, Tcl command GIF glob options added in Tcl 8.3 glob, string matching glob, Tcl command global arrays 2nd global binding global scope and the variable command global, Tcl command goto grab, Tk command Grabbing the Focus Graphic Protocol Errors, Handling Graphical Applications with Tcl & Tk, book graphics context 2nd gravity of text marks grid -ipadx and -ipady -padx and -pady added in Tk 4.1 basic example changes in Tk 4.2 changes in Tk 8.0 external padding
grid internal padding grid minimum size multiple widgets in a cell resize behavior row and column constraints row and column padding row and column span 2nd row and column specifications sticky settings 2nd text widget and scrollbar Tk command 2nd gridded geometry gridded geometry for a canvas geometry:gridding, canvas gridded geometry, in C gridded, resizable listbox gridding, listbox grips on corners grouping rules guard data with, Tcl_Preserve and Tcl_Release Guestbook CGI Application guestbook.cgi script guestbook.cgi, output 2nd
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Handlers, Exit Handlers, File Handling Graphic Protocol Errors Handling Window Events Harrison, Mark 2nd hash table hash table package, in C Hash Table, Creating and Removing Elements Hash Tables, Creating and Destroying hbox, window layout HEAD, HTTP Header Files, Standard Hello, World! Tk program Hello, World!, canvas example Help menu help pop-up Hidden Commands Hidden Text high resolution timer History Syntax, Tcl History, command history, Tcl command horizontal and vertical layout, nested horizontal fill in a menu bar horizontal window layout hostname Hot Tip abbreviate history commands args and eval array elements on demand array for module data attribute resource names Big lists can be slow bind callback is in global scope borders, padding, and highlight braces get stripped off
button command procedures canvas stroke example careful file open color, convert to RGB comments at the end of line comments in switch curly brace placement debug Tk with tkinspect do not declare Tcl variables entry, displaying end of string errors on close eval and double quotes expr is unreliable for string comparison find file by name global inside procedures group command bodies with braces for safety group expressions with braces group expressions with braces for performance grouping before substitution I/O operations and fileevent labels that change size list handling in C code list with foreach list, after, and send menu accelerator, consistent menu index pattern message text layout mouse cursors, all shown open a process pipeline pack the scrollbar first packing widgets to a side pipelines and error output procedures to hide list structure quotes lose list structure resources, general patterns first result string, managing in C scrollbar for two listboxes scrolling widgets on a canvas send requires X authority send, constructing commands single round of interpretation size not set until mapped string conversions by expr Tcl_Eval may modify its string text mark gravity The list command does automatic quoting tkwait on global variable traces on entry values trapping errors from pipelines update, using safely Use arrays to collect related variables variable for widget name
virtual root window coordinates widget data, safety in C window session protocol window size, getting correct Hot Tip:font, fall back to fixed font:fall back to fixed Hot Tip:Tcl_Obj initial reference count is zero reference count, zero initial value Tcl_Obj reference count How Auto Loading Works HTML A Quick Introduction comments, removing Dynamic Pages entity decoder form, self-checking Page, Beginning Simple Parser Tcl template file 2nd templates, for site structure templates, form handlers Html_DecodeEntity Html_Parse http HTTP download HTTP, network protocol http\ \ cleanup config formatQuery geturl reset Http_Get fetches the contents of a URL Http_Head validates a URL HttpCopyDone is used with fcopy HttpGetText reads text URLs Hypertext Links, Simple Tags
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] I/O Channel Drivers I/O events, in C 2nd icon Macintosh and Windows ID Management, X Resource Identifiers (UIDS), String idle events, in C 2nd 3rd if, Tcl command image C interface 2nd C interface, photo in text widget on canvas Tk command widget attribute images, and namespaces Img Patch Implementing join in Tcl Importing and Exporting Procedures inch incr procedure, improved incr Tcl From The Ground Up, book[] incr Tcl object system incr Tcl, object system incr, Tcl command 2nd increment a variable index canvas text object of loadable packages text widget infinite loop info hostname info, Tcl command init.tcl, location during startup Initialization Procedure, Package Initialization, Application
Initialization, Tcl Initializing the Extension input data into Tcl lists input focus 2nd 3rd input focus highlight Input/Output Binary Data channels, stacking configuration events, in C from C from Safe Interpreters redirection Input/Output Command Summary Inserting and Deleting Text inspector, tkinspect Installation Directory Structure Installation, Tcl integrating TclHttpd with your application interactive command entry Interactive Command History Interface, DString Interface, String Command Interface, Tcl_Obj Command internal padding, -ipady Internationalization 2nd 3rd 4th Internet Explorer interp, Tcl Command interpreter and namespaces creating creating in C data structures, AssocData hierarchy of internal state name as a command tracing commands in C interprocess communication. [See send.] intranet applications, Tcl plug-in Introduction to HTML Introspection introspection, with pack Invoking Scripts from C italic text 2nd itcl, object-oriented Tcl Ivler, John 2nd
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Java Javascript Javascript, access from Tcl plug-in Johnson, Eric join in Tcl, Implementing Join Styles, Line join, Tcl command JPEG justification in text widget justification of text in C
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Keeping References to Tcl_Obj Values keyboard Bindings and Focus events focus traversal grab Key events map selection, menu shortcuts and focus Kupries, Andreas
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] label and an entry arranging attributes displaying different strings lining up 2nd multiline size Tk widget width and wrap length lappend, Tcl command lassign, list assignment with foreach. ldelete, Tcl procedure Libes, Don 2nd 3rd library and imported commands, loading Based on the tclIndex File directory, Tcl how it works index introduction of procedures regular expression C search path shared Tcl C 2nd Tk C using stubs Limitations of exec on Windows Limited Socket Access Limited Temporary Files lindex, Tcl command Line Cap Styles Line Join Styles line on canvas line segment performance
Line Spacing and Justification Link, formats a hypertext link linking, dynamic linsert, Tcl command list and concat append elements assignment into automatic quoting comparison with double quotes and concat constructing commands 2nd 3rd 4th 5th 6th convert to array delete by value implement a stack insert elements join into string length of manipulation in C code 2nd modifying of arrays performance of splice together split string into sublist summary of operations syntax Tcl command with foreach with interp eval list example listbox attributes bindings geometry gridding pair working together selecting items Tk 4.0 changes Tk widget with optional scrollbars listbox attributes listbox bindings listbox operations listbox, pair of listen, sockets Listing commands defined by a namespace llength, Tcl command load automatic package changes in Tk 4.1 into slave interpreters Tcl Command Tcl command 2nd
Loadable Package, Creating loadable package, initialization Loading and Packages, Dynamic Loading Option Database Files Locale, Specifying a Locating Packages, auto_path Variable Locating the Tcl Script Library log files, TclHttpd Log Procedure Logging the output of a program Look-ahead in regular expressions Looking at all widget attributes Looking at Marks Looking at Tags Lookup, Command loop break & continue event for foreach reading input while LoVerso, John lower, Tk command lrange, Tcl command lreplace, example lreplace, Tcl command lsort, changes in Tcl 8.0 lsort, Tcl command
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Macintosh Apple menu auto_path command key file types look and feel Plugin Configuration serial devices shared libraries shared library location source Tcl from resource system font size unsupported1 Command window styles. zoom, full Mackerras, Paul main program and Tcl_AppInit, Tcl main program and Tk_AppInit, Tk Main Programs and Command-Line Arguments Makefile Templates Makefile.in 2nd Management, Geometry Management, X Resource ID Managing Bitmaps Managing Global State Managing Message Catalog Files Managing Tcl_Obj Reference Counts Managing the Result String 2nd Managing User Preferences Manipulating File Names Manipulating Files and Directories Manipulating Menus and Menu Entries Manipulating Tcl Variables manual, on-line Map, window event Mapping Strings
mapping windows MapWindow mark gravity, text mark position in text 2nd Marshall Rose Matching Characters Matching File Names with glob Matching Precedence Matching, Regular Expressions and String math expressions math functions, built in trig functions McLennan, Michael 2nd Measurements, Screen Memory Allocation menu accelerator accelerator linked to bindings apple attributes for widget button for by name package cascade cascaded menu helper procedure define with resource database entries via resources example of different types example, screen shot help keyboard selection menubar menubuttons multicolumn palette operations on entries packing a menubar pop-up system Tk widget user defined menubutton, Tk widget MenuGet maps from name to menu message arranging text formats long lines of text text layout Tk widget Message Box message catalogs and namespaces example managing Meta key
Michael McLennan Microsoft VC++ compiler millimeters MIME type min max scale, example minimize, window operation missing font Modifiers, Event Modifying Lists Modifying Tcl_Obj Values module data Module Prefix for Procedure Names module support mouse cursor cursor on Windows event coordinates events Mouse Cursors MouseWheel event msgcat package 2nd msgcat\ \ mcunknown Multicolumn Palette Menus multiline labels Multiple foreach loop variables Multiple Interpreters Multiple Interpreters and Safe-Tcl Multiple Interpreters andSafe-Tcl Multiple loop variables with foreach multiple return values multiple selection mode, listbox Multiple value lists with foreach multiway branch, switch
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] name giving out yours manipulating file of color of I/O channels of interpreter, in C of interpreter, send 2nd 3rd of procedure of variable of widgets qualified namespace quirks in namespaces Named Fonts namespace and Callbacks and uplevel and upvar and Widgets, Images, and Interpreters Converting Packages to use listing commands message catalogs Nested Using Variables Namespaces Native Buttons and Scrollbars Native Look and Feel Native Menus and Menubars nested frames Nested Namespaces Netscape Navigator Network programming network server Network Sockets New Image Types, Creating newguest.cgi script
newguest Form Newline Sensitive Matching Newline Translations Nichols, David Nijtmans, Jan nonblocking I/O nongreedy quantifiers Numbers, Converting numeric value, widget for
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Object Support, Canvas Object Types, Primitive Object Types, String object-oriented, incr Tcl Objects with Many Points Objects, C Command Procedures and Data Objects, Tcl Octal Numbers ODBC on-line manual open a window a window, binding catching errors from file for an unsafe interpreter file for writing files for I/O Process Pipeline Tcl command Option Database. [See resource.] Option Menus Option Processing option, Tk command optional scrollbars Oracle OraTcl Order, Window Stacking Ousterhout, John 2nd oval, canvas object
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] pack binary data display space expand vs. fill into other relatives nested frames order of children packing order 2nd Packing Space and Display Space padding padding vs. button padding resizing windows scrollbars first space for Tk 4.0 changes Tk command unexpected results package convert to namespaces creating a loadable dynamic loading implemented in C code index index file initialization in C Tcl command 2nd version numbers padding and anchors around widgets button vs. packer in buttons provided by labels and buttons widget palette menus Pane Manager
Pane_Create sets up vertical or horizontal panes PaneDrag adjusts the percentage PaneGeometry updates the layout parameters, variable number of parent for pack parity, serial interface parsing arguments Parsing Arguments and Tcl_GetIndexFromObj parsing command-line arguments Parsing Tcl Commands in C pass by reference Passing Arrays by Name paste, canvas example Paste, virtual event pattern match glob, file name match glob, string match menu entries resource database switch command performance canvas items faster string operations improving expression tuning performance tuning Perl photo, C interface pid, Tcl command pie slice, canvas pipes and errors closing fileevent setting up in C Pitfalls of Shared Tcl_Obj Values pixels per inch pixmap, in C pixmap, off screen pkg_mkIndex, Tcl command 2nd place basics Place Geometry Manager place, Tk command Platform-Independent Fonts Platform-Specific End of Line Characters plugin configuration Macintosh UNIX Windows environment variables examples
Tcl 8.2 support plus, in bindings Plus1ObjCmd procedure Pointer Warping points per pixel polygon, canvas item Pop-Up Menus position in text widget position, relative to widget Positioning a window above a sibling Positions, Text Anchor POSIX file access POSIX, errorCode POST, form data POST, HTTP protocol postscript from canvas precision, of expressions Predefined Variables Pref_Add Pref_Dialog Pref_Init PrefDialogItem PrefDismiss PrefEntrySet preferences data definition help initialization items, adding read from file saving to file user interface variables PrefFixupBoolean PrefItemHelp PrefReadFile PrefReset PrefSave PrefValue PrefValueSet Preserving errorInfo when calling error PRIMARY selection Primitive Object Types print [See puts] Print variable by name PrintByName, Tcl procedure printer points printf [See format command] Printing environment variable values private procedure proc, Tcl command 2nd procedure
array parameters as parameter characters allowed in names construct dynamically definition 2nd Importing and Exporting introspection library multiple return values naming conventions query definition to build dialogs variable scope process ID Processing HTML Form Data profiling Tcl code program and Tcl_AppInit, Tcl main program and Tk_AppInit, Tk main program arguments, TclHttpd program output, saving Programming and Tcl, C Programming Entry Widgets Programming Listboxes Programming Scales Programming Scrollbars Prompter Dialog Prompting for input property of widget [See attribute] Protocol Errors, Handling Graphic provide/require package model proxy, web server puts puts, limited with safe interp puts, Tcl command pwd, Tcl command
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Qualified Names Quantifiers in regular expressions Querying aliases questhead Quirks, Namespaces quit application, protocol to quit button quotes compared to concat and list quoting and eval 2nd quoting and regular expressions quoting tips, funny values quoting tips, grouping quoting, automatic
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] radiobutton, Tk widget Raines, Paul raise an error raise, Tk command Raising an error with return Random Access I/O random access I/O random number example Random number example using namespaces 2nd random, in C RandomCmd C command procedure RandomObjCmd C command procedure read commands from a socket file line by line I/O in background option database file Tcl command Tcl commands from a file 2nd read-only entry widget read-only text widget read-only variables readline [See gets] realloc records, with arrays rectangle on canvas redefining procedures redisplay. [See update.] redo [See history] Reference Counts, Managing Tcl_Obj References to Tcl_Obj Values, Keeping Referencing an array indirectly Reflection and Debugging regexp, Tcl command register image format, in C regsub -subst or -command
regsub, counting with regsub, Tcl command regular expression C library Regular Expression Syntax Regular Expressions 2nd regular expressions, new for Tcl 8.1 Reid, Steve relative and absolute window sizes relative position of windows Relief Style relief, widget attribute Remote eval using sockets Removing Elements from a Hash Table rename, Tcl command Renaming Files and Directories Reporting Script Errors ResEdit, Macintosh Resizable Text and Scrollbar Resizing and -expand resizing windows 2nd resizing windows, effect of expand resource associated with Tcl variable attribute names class color database access database description example file example font for canvas objects ID Management, X introduction loading from files 2nd lookup in C code Macintosh name patterns non-standard names order of patterns specifications user vs. application with variable references Resource_ButtonFrame Resource_ButtonFrame defines buttons Resource_GetFamily merges resources RESOURCE_MANAGER property Result Codes from Command Procedures result string, managing in C 2nd Return key return multiple values return, Tcl command 2nd
reverse video RGB color values ring the bell Rooms [See Virtual Root] Rose, Marshal rotation not supported, canvas row of buttons RS 232, serial devices rubber banding Run Procedure Running Programs with exec runs Tcl commands from C, Tcl_Eval
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Safe after Command Safe Base Safe Interpreters Safe-Tcl 2nd 3rd Safe-Tk and the Browser Plugin Safe-Tk Restrictions safe\ \ loadTk Safesock security policy Sample Extension sample message catalog files Sample Regular Expressions saving preferences saving state as Tcl commands scale of screen units scale widget attributes bindings canvas implementation operations Tk widget variable scaling canvas objects scan, Tcl command scanf [See scan command] Schroeder, Hattie scope, for callbacks scope, global variables scope, local variables screen coordinates vs. canvas coordinates for toplevel measurement units 2nd in C scaling
multiple relative coordinates Script Errors, Reporting script library initialization setting with TCL_LIBRARY using script, current Scriptics FTP site Scripts and the Library Scripts from C, Invoking 2nd Scripts in Different Encodings Scroll_Set manages optional scrollbars scrollbar attributes automatic hiding bindings example 2nd for canvas for listbox for two widgets operations protocol with widgets protocol, Tk 3.6 set size widgets on a canvas with text ScrollFixup search path, library 2nd search text widget searching arrays Searching Lists Searching through Files secure hash algorithm, sha1 Secure sockets security policies and Browser Plugin configuration 2nd creating feature sets Safesock Tempfile seek, Tcl command select [See fileevent] selection and clipboard in C canvas example 2nd CLIPBOARD exporting to X handler example model ownership
PRIMARY text widget 2nd Tk command self-checking form send application name in C command to another application constructing command reliably name of interpreter 2nd timeout changed in Tk 4.0 Tk command X authority required sender application Serial Line I/O 2nd serial ports Server Socket Options Server Sockets session, window system Set Conversions, Character set, Tcl command 2nd setgrid, widget attribute sha1, secure hash algorithm shared libraries 2nd 3rd Shared Tcl_Obj Values, Pitfalls of Shen, Sam Shrinking Frames and pack propagate signal handling, in C Signals, Working with significant digits Simple Records single selection mode, listbox SiteFooter SiteMenu SitePage size of label size, relative to widget sleep [See after] sleep, Tcl_Sleep Smith, Chad socket accepting connections added in Tcl 7.5 client side limited with safe interp listen read Tcl commands from server example Tcl Command sort sorting lists source example
in safe interpreters loading code into TclHttpd Tcl command 2nd source code compiling Tcl from distribution, Tcl space around widgets spaces in array indices spacing, widgets Specifying a Locale Spencer, Henry splice lists together spline curve on canvas split data into Tcl lists split, Tcl command square brackets SSL channel plugin stack trace 2nd stack, data structure stack, example stacking I/O channels stacking order, window 2nd 3rd standalone executable standalone Tcl script Standard Configure Flags Standard Dialogs 2nd Standard Directory Structure Standard Header Files standard options static code checker Status, Tcl procedure stderr, standard error output stdin, standard input stdout, standard output Sticky Geometry Settings stop bits, serial interface Stop Procedure strftime string characters from command interface in C comparison 2nd comparison, using expr concatenate display. [See label. ] dynamic (DString) in C effect of backslash-newline expressions identifiers (UIDS) in C indices internationalization in C mapping to new strings
matching matching, regular expressions in C object types in C processing with subst result, managing in C 2nd Tcl command string command, changes in Tcl 8.1 stroke, canvas example structures, with arrays Stub Libraries, Using Stump, John Style, Relief Styles, Line Cap Styles, Line Join Styles, Text Justification subst, Tcl command subst, template example substitution rules substitution, before grouping Substitutions and hidden commands substitutions, no eval Summary of Package Loading Summary of the Tk Commands Sun Microsystems SWIG switch example on exact strings substitutions in patterns Tcl command with fallthrough cases Sybase SybTcl syntax arrays command curly braces dollar sign list 2nd square brackets TclPro Checker System Encoding system font System Menus
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Tab key Tab Stops tab, default binding Table, Hash 2nd tag canvas widget 2nd HTML text widget 2nd attributes bndings taskbar TChar Tcl 7.5 Tcl 7.6 2nd Tcl 8.0 Patch Releases Tcl and Extensions, Compiling Tcl and the Tk Toolkit, book Tcl and Tk sources, on the web Tcl C Library 2nd Tcl command after, timer events append, strings array, data type bgerror, error handler 2nd binary, convert between string and binary break, exit loop catch, error handler cd, change directory close, I/O channel concat, concatenate strings and lists 2nd console, Windows and Macintosh 2nd continue, loop encoding, character sets eof, test end of file error, raise error eval, evaluate a string
exec, run programs exit, terminate expr, math expressions fblocked, I/O channel fconfigure, I/O channel properties fcopy, I/O channel copy file, operate on files fileevent, select I/O channel 2nd flush, I/O channel for, loop foreach, loop format, strings 2nd from C, Tcl_Eval 2nd gets, read line glob, match file names global, variables history, of commands if, conditional incr, improved version incr, increment variable info, introspection interp, create interpreter join, merge lists lappend, append to list linsert, modify list list, create lists list-related commands llength, list length load, compiled extensions lrange, list sublist lsort, sort list namespace, variables and procedures number executed open, I/O channel package, manage libraries pid, get process ID pkg_mkIndex, generate package index proc, define procedures 2nd puts, print line pwd, get working directory read, I/O channel regexp, match regular expression regsub, regular expression substitution rename, commands an d procedures return, from procedure 2nd scan, parsing strings seek, move I/O channel offset set, getting variable value socket, network source, read Tcl script file split, data into list subst, substitute Tcl in data
switch, multiway branch table of tell, read I/O channel offset time, measure command speed Tk command trace, variables unknown, command fallback unset, delete variable uplevel, evaluate in different scope upvar, variable references vwait, wait for event while, loop writing commands to files Tcl Extension Architecture Tcl Initialization Tcl library Tcl main program and Tcl_AppInit Tcl Objects Tcl Scripts, Calling Out to Tcl Shell Library Environment Tcl shell, sample program 2nd Tcl Style Guide Tcl variable argv 2nd 3rd 4th 5th auto_noexec 2nd auto_noload auto_path 2nd 3rd 4th env errorCode errorInfo 2nd from C tcl_library 2nd tcl_pkgPath tcl_precision Tcl version Tcl, C Programming and Tcl, compiling from source 2nd Tcl, CVS repository tcl.m4 autoconf macros Tcl/Tk 8.0 2nd Tcl/Tk 8.1 2nd Tcl/Tk 8.2 2nd Tcl/Tk 8.3 Tcl/Tk for Programmers, book Tcl/Tk for Real Programmers, book Tcl/Tk in a Nutshell, book Tcl/Tk Tools, book Tcl_Access 2nd Tcl_AddErrorInfo Tcl_AddObjErrorInfo Tcl_Alert-Notifier Tcl_Alloc 2nd
Tcl_Alloc and Tcl_Free Tcl_AllowExceptions Tcl_AppendAllObjTypes Tcl_AppendElement 2nd Tcl_AppendObjToObj 2nd Tcl_AppendResult 2nd 3rd 4th 5th Tcl_AppendStringsToObj 2nd Tcl_AppendToObj Tcl_AppendUnicodeToObj Tcl_AppInit 2nd 3rd Tcl_AsyncCreate Tcl_AsyncDelete Tcl_AsyncInvoke 2nd Tcl_AsyncMark Tcl_AsyncReady Tcl_BackgroundError Tcl_Backslash Tcl_BadChannelOption Tcl_CallWhenDeleted Tcl_CancelIdleCall Tcl_Chdir Tcl_Close Tcl_CommandComplete Tcl_Concat 2nd Tcl_ConcatObj 2nd Tcl_ConditionNotify Tcl_ConditionWait Tcl_ConvertElement Tcl_ConvertToType Tcl_CreateAlias Tcl_CreateAliasObj Tcl_CreateChannel Tcl_CreateChannelHandler Tcl_CreateCloseHandler Tcl_CreateCommand 2nd 3rd 4th 5th 6th 7th 8th Tcl_CreateEncoding Tcl_CreateEventSource Tcl_CreateExitHandler Tcl_CreateFileHandler 2nd 3rd Tcl_CreateHashEntry 2nd Tcl_CreateInterp 2nd Tcl_CreateMathFunc Tcl_CreateObjCommand 2nd 3rd Tcl_CreateSlave Tcl_CreateThreadExitHandler Tcl_CreateTimerHandler Tcl_CreateTrace Tcl_DbgAlloc and Tcl_DbgFree Tcl_DecrRefCount 2nd 3rd 4th 5th Tcl_Delete-HashEntry Tcl_DeleteAssocData Tcl_DeleteChannelHandler
Tcl_DeleteCloseHandler Tcl_DeleteCommand 2nd 3rd Tcl_DeleteCommandFromToken Tcl_DeleteEvents Tcl_DeleteEventSource Tcl_DeleteExitHandler Tcl_DeleteFileHandler 2nd Tcl_DeleteHashTable Tcl_DeleteInterp 2nd Tcl_DeleteThreadExitHandler Tcl_DeleteTimerHandler Tcl_DeleteTrace Tcl_DelteHashEntry Tcl_DetachPids Tcl_DiscardResult Tcl_DontCallWhenDeleted Tcl_DoOneEvent 2nd Tcl_DoWhenIdle Tcl_DString Tcl_DString-Result Tcl_DStringAppend 2nd Tcl_DStringAppendElement Tcl_DStringEndSublist Tcl_DStringFree Tcl_DStringGetResult Tcl_DStringInit 2nd Tcl_DStringLength Tcl_DStringSetLength Tcl_DStringStartSublist Tcl_DStringValue 2nd Tcl_DuplicateObj 2nd Tcl_Eof TCL_ERROR Tcl_Eval 2nd 3rd Tcl_Eval modifies its argument. Tcl_Eval, Bypassing TCL_EVAL_DIRECT Tcl_EvalEx 2nd Tcl_EvalFile Tcl_EvalObj 2nd Tcl_EvalObjEx 2nd Tcl_EvalObjv 2nd Tcl_EvalTokens Tcl_EventuallyFree 2nd Tcl_Exit 2nd Tcl_ExposeCommand Tcl_ExprBoolean Tcl_ExprBooleanObj Tcl_ExprDouble Tcl_ExprDoubleObj Tcl_ExprLong Tcl_ExprLongObj
Tcl_ExprObj Tcl_ExprString Tcl_ExternalToUtf Tcl_ExternalToUtfDString 2nd Tcl_Finalize Tcl_FinalizeNotifier Tcl_FinalizeThread Tcl_FindExecuatable Tcl_FindHashEntry tcl_findLibrary 2nd Tcl_FirstHashEntry 2nd 3rd Tcl_Flush Tcl_Free 2nd Tcl_Free, Tcl_Alloc and Tcl_FreeEncoding 2nd Tcl_FreeParse Tcl_GetAlias Tcl_GetAliases Tcl_GetAliasObj Tcl_GetAssocData Tcl_GetBoolean Tcl_GetBooleanFromObj Tcl_GetByteArrayFromObj Tcl_GetChannel Tcl_GetChannelBufferSize Tcl_GetChannelHandle Tcl_GetChannelInstanceData Tcl_GetChannelMode Tcl_GetChannelName Tcl_GetChannelOption Tcl_GetChannelType Tcl_GetCharLength Tcl_GetCommandInfo 2nd 3rd Tcl_GetCurrentThread Tcl_GetCwd Tcl_GetDefaultEncodingDir Tcl_GetDouble Tcl_GetDoubleFromObj Tcl_GetEncoding 2nd Tcl_GetEncodingName Tcl_GetEncodingNames Tcl_GetErrno Tcl_GetHashKey Tcl_GetHashValue 2nd Tcl_GetIndexFromObj 2nd 3rd 4th 5th Tcl_GetInt 2nd 3rd 4th Tcl_GetInterpPath Tcl_GetIntFromObj 2nd Tcl_GetListFromObj Tcl_GetLongFromObj Tcl_GetMaster Tcl_GetNameOfExecutable
Tcl_GetObjResult 2nd 3rd Tcl_GetObjType Tcl_GetOpenFile Tcl_GetPathType Tcl_GetRange Tcl_GetRegExpFromObj Tcl_GetRegExpInfo Tcl_Gets Tcl_GetServiceMode Tcl_GetSlave Tcl_GetSlaves Tcl_GetsObj Tcl_GetStackedChannel Tcl_GetStdChannel Tcl_GetString Tcl_GetStringFromObj Tcl_GetStringResult Tcl_GetThreadData Tcl_GetUniChar 2nd Tcl_GetUnicode Tcl_GetVar Tcl_GetVar2 Tcl_GetVar2Ex TCL_GLOBAL_EVAL Tcl_GlobalEval 2nd Tcl_GlobalEvalObj Tcl_HashStats Tcl_HashTable Tcl_HideCommand Tcl_IncrRefCount 2nd 3rd 4th Tcl_Init Tcl_InitHashTable 2nd Tcl_InitNotifier Tcl_InitStubs 2nd 3rd 4th 5th Tcl_InputBlocked Tcl_InputBuffered Tcl_InterpDeleted Tcl_Invoke Tcl_Invoke bypasses Tcl_Eval Tcl_IsSafe Tcl_IsShared 2nd Tcl_JoinPath TCL_LIBRARY, environment variable tcl_library, Tcl variable 2nd Tcl_LinkVar Tcl_ListObjAppendElement 2nd Tcl_ListObjAppendList Tcl_ListObjGetElements Tcl_ListObjIndex Tcl_ListObjLength Tcl_ListObjReplace Tcl_Main
Tcl_Main and Tcl_AppInit Tcl_MakeFileChannel Tcl_MakeSafe Tcl_MakeTcpClientChannel Tcl_Merge 2nd Tcl_MutexLock Tcl_MutexUnlock Tcl_NewBooleanObj Tcl_NewByteArrayObj Tcl_NewDoubleObj Tcl_NewIntObj Tcl_NewListObj 2nd Tcl_NewLongObj Tcl_NewObj Tcl_NewStringObj 2nd Tcl_NewUnicodeObj Tcl_NextHashEntry Tcl_NotifyChannel Tcl_NumUtfChars Tcl_Obj Tcl_Obj Command Interface, The Tcl_Obj Reference Counts, Managing Tcl_Obj structure., The Tcl_Obj Values, Keeping References to Tcl_Obj Values, Modifying Tcl_Obj Values, Pitfalls of Shared Tcl_Obj version of Tk widget Tcl_ObjGetVar2 Tcl_ObjSetVar2 TCL_OK Tcl_OpenCommandChannel 2nd Tcl_OpenFileChannel Tcl_OpenTcpClient Tcl_OpenTcpServer Tcl_ParseBraces Tcl_ParseCommand Tcl_ParseExpr Tcl_ParseQuotedString Tcl_ParseVarName tcl_pkgPath, Tcl variable Tcl_PkgPresent Tcl_PkgPresentEx Tcl_PkgProvide 2nd 3rd Tcl_PkgProvide, Using Tcl_PkgProvideExx Tcl_PkgRequire Tcl_PkgRequireEx tcl_platform tcl_platform, debug element tcl_platform, user element Tcl_PosixError tcl_precision
tcl_precision variable tcl_precision, changes in Tcl 8.0 Tcl_Preserve 2nd Tcl_PrintDouble Tcl_QueueEvent Tcl_Read Tcl_ReadChars Tcl_Realloc Tcl_ReapDetachedProcs Tcl_RecordAndEval 2nd Tcl_RegExpCompile Tcl_RegExpExec Tcl_RegExpExecObj Tcl_RegExpMatch Tcl_RegExpMatchObj Tcl_RegExpRange Tcl_RegisterChannel Tcl_RegisterObjType Tcl_Release 2nd Tcl_ResetResult 2nd Tcl_RestoreResult Tcl_SaveResult Tcl_ScanElement Tcl_Seek Tcl_ServiceAll Tcl_ServiceEvent Tcl_SetAssocData Tcl_SetBooleanObj Tcl_SetByteArray-Length Tcl_SetByteArrayObj Tcl_SetChannelBufferSize Tcl_SetChannelOption Tcl_SetCommandInfo 2nd Tcl_SetDefaultEncodingDir. Tcl_SetDefaultTranslation Tcl_SetDoubleObj Tcl_SetErrno Tcl_SetErrorCode Tcl_SetHashValue 2nd Tcl_SetIntObj 2nd 3rd Tcl_SetListObj Tcl_SetLongObj Tcl_SetMaxBlockTime Tcl_SetNotifier Tcl_SetObjLength Tcl_SetObjResult 2nd 3rd 4th Tcl_SetRecursionLimit Tcl_SetResult 2nd Tcl_SetServiceMode Tcl_SetStdChannel Tcl_SetStringObj 2nd 3rd 4th Tcl_SetSystemEncoding
Tcl_SetTimer Tcl_SetUnicodeObj Tcl_SetVar 2nd Tcl_SetVar2 Tcl_SetVar2Ex Tcl_Sleep 2nd Tcl_SplitList Tcl_SplitPath Tcl_StackChannel Tcl_Stat 2nd Tcl_StaticPackage TCL_STORAGE_CLASS Tcl_StringCaseMatch Tcl_StringMatch Tcl_Tell Tcl_ThreadAlert Tcl_ThreadQueueEvent Tcl_TraceVar Tcl_TraceVar2 Tcl_TranslateFileName 2nd Tcl_UniChar Tcl_UniCharAtIndex Tcl_UniCharLen Tcl_UniCharNcmp Tcl_UniCharToLower Tcl_UniCharToTitle Tcl_UniCharToUpper Tcl_UniCharToUtf Tcl_UniCharToUtfDString Tcl_UnlinkVar Tcl_UnregisterChannel Tcl_UnsetVar Tcl_UnsetVar2 Tcl_UnstackChannel Tcl_UntraceVar Tcl_UntraceVar2 Tcl_UpdateLinkedVar Tcl_UpVar Tcl_UpVar2 Tcl_UtfAtIndex Tcl_UtfBackslash Tcl_UtfCharComplete Tcl_UtfFindFirst Tcl_UtfFindLast Tcl_UtfNext Tcl_UtfPrev Tcl_UtfToExternal Tcl_UtfToExternalDString 2nd Tcl_UtfToLower Tcl_UtfToTitle Tcl_UtfToUniChar Tcl_UtfToUniCharDString
Tcl_UtfToUpper TCL_VARARGS_START Tcl_VarEval 2nd Tcl_VarTraceInfo Tcl_VarTraceInfo2 Tcl_WaitForEvent 2nd Tcl_WinTCharToUtf 2nd Tcl_WinUtfToTChar 2nd Tcl_Write Tcl_WriteChars Tcl_WriteObj Tcl_WrongNumArgs 2nd TclBlend, Java integration tclConfig.sh Tclets, network applications TclHttpd adding source code administration architecture document root document type handler domain handler HTML template integrating with application log files quick start configuration self-checking form source code distribution templates URL domain handler tclIndex file TclODBC TclpCreateThread tclPkgUnknown TclPro Checker TclPro Compiler TclPro Debugger TclPro Wrapper tclsh, applicaiton TclX TCP/IP TEA tell, Tcl command Tempfile security policy template binary format configure.in for procedure body HTML Makefile.in SiteMenu and SiteFooter terminate process
text anchor positions, in C attributes attributes for tags attributes from multiple tags bindings 2nd bold bulleted list changes in Tk 4.1 color display and fonts, in C embedded window 2nd find range of tag hidden in a message widget index index arithmetic insert cursor italic justification mark 2nd gravity introspection on canvas operations read-only searching selection tabs tag 2nd bindings introspection Tk 4.0 changes Tk widget two scrollbars underlined ustification, in C widget introspection with scrollbar then [See if.] Thomas, Michael Thread Support 2nd tilde in file names tilde key, asciicircum Time stamps in a Log time, Tcl command 2nd timer [See after] Timer Events timer events, in C 2nd timer, high resolution title, of window Tix
Tk 4.0, porting issues Tk 4.1, porting issues Tk 4.2, porting issues Tk 8.0, porting issues Tk 8.1, porting issues Tk 8.2, porting issues Tk 8.3, porting issues Tk by Example Tk C Library tk colormodel, removed in Tk 4.0 Tk command [See also widgets.] bell bind, events bindtags, binding groups clipboard, cut and paste destroy, window event, generation focus, on window font, control grab, focus grid, geometry manager image, manipulation lower, window option, resource database pack, geometry manager place, geometrymanager raise, window selection, cut and paste send, command to application tk, miscellaneous tkerror tkwait, for event unsupported1, window styles update, events winfo, window info wm, window control Tk Command Summary Tk Fundamentals Tk in Child Interpreters Tk main program and Tk_AppInit Tk Manual Pages tk scaling Tk Widget Attributes and the Resource Database tk, Tk command Tk_3DBorderColor Tk_3DBorderGC Tk_3DHorizontalBevel Tk_3DVerticalBevel Tk_Alloc3DBorderFromObj Tk_AllocBitmapFromObj Tk_AllocColorFromObj Tk_AllocCursorFromObj
Tk_AllocFontFromObj Tk_AppInit, Tk main program and Tk_Attributes Tk_BindEvent Tk_CancelIdleCall 2nd Tk_CanvasDrawableCoords Tk_CanvasEventuallyRedraw Tk_CanvasGetCoord Tk_CanvasGetTextInfo Tk_CanvasPsBitmap Tk_CanvasPsColor Tk_CanvasPsFont Tk_CanvasPsPath Tk_CanvasPsStipple Tk_CanvasPsY Tk_CanvasSetStippleOrigin Tk_CanvasTagsOption Tk_CanvasTkwin Tk_CanvasWindowCoords Tk_Changes Tk_ChangeWindowAttributes Tk_CharBbox Tk_Class Tk_ClearSelection Tk_ClipboardAppend Tk_ClipboardClear Tk_Colormap Tk_ComputeTextLayout 2nd Tk_ConfigSpec 2nd Tk_ConfigSpec typedef Tk_ConfigureInfo 2nd Tk_ConfigureValue 2nd 3rd Tk_ConfigureWidget 2nd Tk_ConfigureWindow Tk_CoordsToWindow Tk_CreateBinding Tk_CreateBindingTable Tk_CreateErrorHandler Tk_CreateEventHandler 2nd 3rd Tk_CreateGenericHandler 2nd Tk_CreateImageType Tk_CreateItemType Tk_CreateOptionTable Tk_CreatePhotoImageFormat Tk_CreateSelHandler Tk_CreateTimerHandler Tk_CreateWindow Tk_CreateWindowFromPath 2nd 3rd Tk_DefineBitmap Tk_DefineCursor Tk_DeleteAllBindings Tk_DeleteBinding
Tk_DeleteBindingTable Tk_DeleteErrorHandler Tk_DeleteEventHandler Tk_DeleteGenericHandler Tk_DeleteImage Tk_DeleteOptionTable Tk_DeleteOptionTable Tk_DeleteSelHandler Tk_DeleteTimerHandler 2nd Tk_Depth Tk_DestroyWindow 2nd tk_dialog, built-in dialog Tk_Display 2nd Tk_DisplayName Tk_DistanceToTextLayout Tk_DoWhenIdle 2nd 3rd Tk_Draw3DPolygon Tk_Draw3DRectangle 2nd Tk_DrawChars Tk_DrawFocusHighlight 2nd Tk_DrawTextLayout 2nd Tk_EventuallyFree Tk_Fill3DPolygon Tk_Fill3DRectangle 2nd Tk_FindPhoto tk_focusFollowsMouse tk_focusNext Tk_FontId Tk_FontMetrics Tk_Free3DBorder Tk_Free3DBorderFromObj Tk_FreeBitmap Tk_FreeBitmapFromObj Tk_FreeColor 2nd Tk_FreeColorFromObj Tk_FreeColormap Tk_FreeConfigOptions Tk_FreeCursor Tk_FreeCursorFromObj Tk_FreeFont Tk_FreeFontFromObj Tk_FreeGC 2nd Tk_FreeImage Tk_FreeOptions 2nd Tk_FreePixmap 2nd 3rd Tk_FreeSavedOptions 2nd Tk_FreeTextLayout Tk_FreeXId Tk_GCForColor Tk_GeometryRequest 2nd Tk_Get3DBorder Tk_Get3DBorderFromObj
Tk_GetAllBindings Tk_GetAnchor Tk_GetAnchorFromObj Tk_GetAtomName Tk_GetBinding Tk_GetBitmap Tk_GetBitmapFromData Tk_GetBitmapFromObj Tk_GetCapStyle Tk_GetColor Tk_GetColorByValue Tk_GetColorFromObj Tk_GetColormap Tk_GetCursor Tk_GetCursorFromData Tk_GetCursorFromObj Tk_GetFont Tk_GetFontFromObj Tk_GetFontMetrics Tk_GetGC 2nd Tk_GetImage Tk_GetImageMasterData Tk_GetItemTypes Tk_GetJoinStyle Tk_GetJustify Tk_GetJustifyFromObj Tk_GetMMFromObj Tk_GetOption Tk_GetOptionInfo 2nd Tk_GetOptionValue Tk_GetPixels Tk_GetPixelsFromObj Tk_GetPixmap 2nd Tk_GetRelief Tk_GetReliefFromObj Tk_GetRootCoords Tk_GetScreenMM Tk_GetScrollInfo Tk_GetSelection Tk_GetString Tk_GetUid Tk_GetVisual Tk_GetVRootGeometry Tk_HandleEvent Tk_Height Tk_IdToWindow Tk_Image Tk_ImageChanged Tk_Init 2nd Tk_Init procedure Tk_InitOptions 2nd Tk_InitStubs
Tk_InternalBorderWidth Tk_InternAtom Tk_IntersectTextLayout Tk_IsMapped 2nd Tk_IsTopLevel tk_listboxSingleSelect Tk_Main 2nd Tk_MainLoop 2nd Tk_MaintainGeometry Tk_MainWindow Tk_MakeWindowExist Tk_ManageGeometry Tk_MapWindow Tk_MeasureChars 2nd Tk_MoveResizeWindow Tk_MoveToplevelWindow Tk_MoveWindow Tk_Name Tk_NameOf3DBorder Tk_NameOfAnchor Tk_NameOfBitmap Tk_NameOfCapStyle Tk_NameOfColor Tk_NameOfCursor Tk_NameOfFont Tk_NameOfImage Tk_NameOfJoinStyle Tk_NameOfJustify Tk_NameOfRelief Tk_NameToWindow Tk_Offset Tk_OptionSpec Tk_OptionSpec typedef Tk_OptionTable Tk_OwnSelection Tk_Parent Tk_ParseArgv 2nd Tk_PathName 2nd Tk_PhotoBlank Tk_PhotoExpand Tk_PhotoGetImage Tk_PhotoGetSize Tk_PhotoHandle Tk_PhotoPutBlock Tk_PhotoPutZoomedBlock Tk_PhotoSetSize Tk_PointToChar Tk_PostscriptFontName Tk_QueueWindowEvent Tk_RedrawImage Tk_ReqHeight Tk_ReqWidth
Tk_ResizeWindow Tk_RestackWindow Tk_RestoreSavedOptions 2nd Tk_RestrictEvent Tk_Screen Tk_ScreenNumber Tk_SetAppName Tk_SetBackgroundFromBorder 2nd Tk_SetClass 2nd 3rd Tk_SetGrid Tk_SetInternalBorder 2nd Tk_SetOptions 2nd Tk_SetWindowBackground 2nd Tk_SetWindowBackgroundPixmap Tk_SetWindowBorder Tk_SetWindowBorderPixmap Tk_SetWindowBorderWidth Tk_SetWindowColormap Tk_SetWindowVisual Tk_SizeOfBitmap Tk_SizeOfImage Tk_TextLayoutToPostscript Tk_TextWidth Tk_UndefineCursor Tk_UnderlineChars Tk_UnderlineTextLayout Tk_UnmaintainGeometry Tk_UnmapWindow Tk_UnsetGrid Tk_Visual Tk_Width Tk_Window 2nd Tk_WindowId Tk_X Tk_Y tkerror, Tcl procedure TkInit tkinspect, inspector program tkman, UNIX program tkwait, Tk command toplevel attributes Tk widget window styles trace example for preferences execution, in C Tcl command variables, in C Transforming Data to Program with regsub Transparent Fill on Canvas Text Tranter, Jeff
Trapping errors from pipelines Trf Patch triple click troublesome button command Tuba, debugger application turn data into list Turning off geometry propagation two screens type conversions are automatic Tcl_Obj in C typeface [See font]
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Uhler, Stephen 2nd underlined text Unicode and Internationalization Unicode and UTF-8 Unicode Fonts UNIX look and feel UNIX Plugin Configuration UNIX Tcl Scripts UNIX to DOS unknown, in safe interpreters unknown, Tcl command unmap window unmap window event unpack binary data unset, Tcl command unsupported1, Tk command 2nd untrusted scripts update, Tk command uplevel and namespaces uplevel, Tcl command upvar example 2nd namespaces Tcl command URL access from Tcl copy to file CVS repository Decoding domain handler, TclHttpd fetch with HTTP implementing by a program Tcl source location User and Group ID user customization user feedback
user interface to preferences User-Defined Buttons User-Defined Menus UTF-8 and Unicode 2nd
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] value, delete list element by Values, Keeping References to Tcl_Obj Values, Modifying Tcl_Obj Values, Pitfalls of Shared Tcl_Obj variable aliases with upvar args argv 2nd 3rd argv0 array auto_noexec 2nd auto_noload auto_path 2nd 3rd call by name characters allowed in names command at global scope command line arguments currently defined declaring deleting embed_args environment errorCode errorInfo 2nd for button for entry text for label text for scale widget from preferences increment manipulate from C code names namespace pass by reference plugin predefined, list of
print by name PrintByName read-only scope and procedures Tcl command tcl_library 2nd tcl_pkgPath tcl_platform tcl_precision test if defined trace access Variable number of arguments vbox, vertical layout version number, Tcl Version Numbers versions, of packages 2nd vertical layout Virtual Events virtual events for cut, copy, and paste virtual root window visual class, widget attribute volume, of bell vwait, Tcl command
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] wait for Tk event Web Programming with Tcl and Perl, book Web server, TclHttpd Web Tcl Complete, book When to Use Regular Expressions while loop to read input while, Tcl command white space widget attributes attributes, in C C data structure class command, in C class definition cleanup in C container [See frame] data structure, in C definition destroy destroy, in C display in C embed in text frame for container hide by unmapping image, from C implemented in C instance command, in C introduction naming reconfigure spacing between Tcl_Obj version unmapping widgets and namespaces button
checkbutton frame label listbox menu menubutton message radiobutton scale text toplevel window activate binding on open changes size close callback configuration in C coordinates, in C creating in C deleting detached embedded in canvas embedding events, in C 2nd family relationships general information in C hierarchy icon bitmap layout in binding manager 2nd maximized minimize resize, interactive rooms session state stacking order stacking order, in C styles taskbar title 2nd virtual root window:gridding geometry:gridding, canvas Windows and exec problems auto_path com ports DLL location look and feel mouse cursor Plugin Configuration shared libraries
Start and Menu Keys Start Menu system font size system menu text mode files winfo, Tk command wish, application wm, Tk command WM_DELETE_WINDOW Working with Signals World Wide Web Writing a Tk Widget in C
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] X authority, send X Font Names X ID for resource, in C X protocol errors X resource database. [See resource. ] X Resource ID Management XCopyArea Xdefaults. [See resource. ] Xerox PARC xfontsel, UNIX program xlsfonts, UNIX program xmodmap, program xrdb [See resource] xset program XSynchronize xview and yview scrollbar perations
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Year 2000 Compliance
Top
Table of Contents
[SYMBOL] [A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z] Zimmer, Adrian zoom zoom box style
Top