|
mark roseman Internet collaboration software and more. | ||||||||||||||||||||||||||||||
|
A Busy Developer's Guide to Tcl/Tk 8.5 Current Status of this Document: This document is current as of the production 8.5.0 release. Last update December 21, 2007. This document is intended to quickly introduce "mainstream" developers who have been using earlier versions of Tcl/Tk to the most important new features in Tcl/Tk 8.5 that they need to know about. This isn't a manager's high-level guide, but covers things that affect your code on a daily basis. It's also not something that would interest the type of people who follow daily commits to the Tcl/Tk CVS repositories. I'm not going to cover every features (and there are lots!), and many things that aren't covered or are covered in less detail are very important to some people; my idea of "mainstream" developer is purely subjective. If you want more comprehensive information, it's available elsewhere. A good starting point is the Changes in Tcl/Tk 8.5 page on the Tcler's Wiki. Tcl 8.5:
|
| Before 8.5: To pass arrays around, you were forced to either use the "upvar" wart: | # accept the name of an array proc processarray {arrayName} { upvar $arrayName a; # you just scared off the Tcl newbies... # use $a(name), $a(age), etc... } ... array set data {name Bob age 20 country USA} processarray data ; # eww, have to pass the name |
| Before 8.5: Or, you could use "array get" and "array set" to pass by value, but with tons of copying, which is excruciatingly slow: | # accept the flattened form of the array proc processarray {arraydata} { array set a $arraydata; # copy, copy... # use $a(name), $a(age), etc... } ... array set data {name Bob age 20 country USA} processarray [array get data]; # copy, copy, copy... |
| With 8.5: While the string representation of dictionaries look like the "array get" form (i.e. list of "key val key val..."), internally they are very efficient. | # accept a dictionary proc processdict {dict} { # ... use [dict get $dict name], [dict get $dict age], etc... } ... set data [dict create name Bob age 20 country USA] processdict $data; # hey, actual pass by value! |
| The expected range of commands to manipulate dictionaries are bundled in the "dict" command. The commands shown here are a useful subset. | dict create ?key value...? dict get dictValue ?key...? dict set dictValue key value dict exists dictValue key ?key...? dict unset dictValue key ?key...? dict replace dictValue ?key value...? dict keys dictValue ?glob? dict values dictValue ?glob? dict for {keyVar valueVar} dictValue body dict size dictVal |
Dictionaries have a lot more features, and include support for things like nested dictionaries, modifying individual values in a dictionary in-place (append, incr, lappend) and more. See TIP #111 or the "dict" manual page for more information.
It is all too common in Tcl and Tk programs that you have a list of several things, that need to be passed as separate parameters to a command. The existing mechanism is ugly, inefficient, error-prone and in fact can even be dangerous. It is a frequent source of problems in even expert Tcl programmers' code.
| Before 8.5: To break up a list into pieces to pass it to a command, you'd use "eval" | set kids [winfo children .] ; #returns a list eval destroy $kids; # destroy needs each window as a separate arg |
| Before 8.5: The actual correct and always safe way to do this is this ugly wart. | set kids [winfo children .] eval [linsert 0 $kids destroy]; # this looks backwards... |
| With 8.5: This can be written more clearly and safely using the new {*} syntax, which breaks up the list in place and passes it as separate arguments to the command. | set kids [winfo children .] destroy {*}$kids |
This is the first new piece of syntax in Tcl in many years (the parser now has 12 rules, not 11). And yes, it's itself an ugly wart, that was the subject of long consideration. However, the problem it solves is so pervasive and damaging that it is unfortunately a much needed change. It's very much worth checking the "eval" calls in your existing code to see if they can be replaced by {*}, and certainly think twice before using eval in the future to explode arguments.
Note that earlier alphas of 8.5 used "{expand}". These should be changed to use "{*}".
This is a small one, but removes one of the most common warts in Tcl programs, checking whether something is a member of a list.
| Before 8.5: While lsearch does the job, it's pretty verbose and indirect, making it harder to read. Plus I can never remember the order of arguments to lsearch. | if {[lsearch -exact $list $item]!=-1} { # item is in the list... } |
| With 8.5: There are new expression operators which let you check if an item is in a list ("in"), or if its not in the list ("ni"). | if {$item in $list} { ... } |
| With 8.5: Again these are small but common things, but expr finally gains "min" and "max" operators, and can now use "**" for exponentiation, which can be more readable than "pow()". | expr {min(1,10,20)} expr {max(2,5)} expr {2.0*x**3 - 1.2*$x**2 + 3.0*$x + 4.0} |
Breaking up a list and storing each element in a separate variable is not hard to do, but it's a very common operation. For example, commands in Tcl can only return a single value, so if they have to return more than one value they pack them all into a list. The caller then has to unpack them.
| Before 8.5: The readable way to do this has always been using multiple "lindex" calls, but for the lover of obscurity, it was also possible with "foreach". | set coords [getBoundingBox $thing] set x0 [lindex $coords 0] set y0 [lindex $coords 1] set x1 [lindex $coords 2] set y1 [lindex $coords 3] foreach {x0 y0 x1 y1} [getBoundingBox $thing] {}; # ewww.... |
| With 8.5: The "lassign" command, originally from TclX, now makes this easy and clear. | lassign [getBoundingBox $thing] x0 y0 x1 y1 |
| Before 8.5: Not hard, but doing counters in Tcl always involved the same dance. | if {![info exists counter]} { set counter 0 } incr counter |
| With 8.5: Now if a variable doesn't exist, incr will auto-initialize it with a value of 0. | incr counter |
| With 8.5: A new "chan" command gathers together a number of operations that work on channels into a single command, to curb some of the global namespace usage for I/O. This deprecates several existing commands, so plan to move to the new versions of those soon. | chan blocked ; # fblocked (DEPRECATED) chan close ; # close chan configure ; # fconfigure (DEPRECATED) chan copy ; # fcopy (DEPRECATED) chan eof ; # eof chan event ; # fileevent (DEPRECATED) chan flush ; # flush chan gets ; # gets chan names ; # file channels (DEPRECATED) chan puts ; # puts chan read ; # read chan seek ; # seek chan tell ; # tell chan truncate ; # ftruncate |
Tcl Modules are an important new mechanism to construct, deliver and deploy Tcl packages. You'll start seeing more and more packages available as Tcl modules, and in particular are the format used by Teapot, the new but long-awaited package repository that Activestate is providing. Yes, after all these years, a central place to locate and download Tcl packages.
Tcl Modules complement, but do not replace the existing package mechanism Because they are simpler, they offer several advantages. Traditional packages consist of several files, with a "pkgIndex.tcl" script pulling the pieces together, identifying the package and version number, etc. Tcl Modules consist of a single, source-able file (with extension ".tm") whose package and version number must be encoded in the filename.
As a result, Tcl Modules are simpler to store (a single file), and much more efficient to search and load (the search paths are more restricted, and only the module's filename needs to be examined).
For more on Tcl Modules, see TIP #189.
Less common or pervasive than the previous changes, the improvements in this section will still be of use in a good number of situations, or, like the new clock command, have undergone such significant change that it warrants being aware of.
Arbitrary-precision integers. Tcl now transparently supports arbitrarily-sized integers (bignums). Rather than generating overflows that became a source of bugs, Tcl now promotes integer to bignums when needed. Of course, this is particularly good if you know you're going to be dealing with large integers, and are (or otherwise would be) using another library to do so. A new "string is wideinteger" command lets you check if a string will fit into a bignum.
New clock command. The "clock" command has been almost entirely rewritten, to deal with a wide range of corner cases, weird input formats, localization issues, platform-specific bugs, bizarre time zones, and more. The new version is almost entirely compatible, unless you were depending on existing bugs. There is also new finer-grained "milliseconds" and "microseconds" options. Note that because the new clock implementation is mostly Tcl, it is somewhat slower than before. As well, Tcl now includes a very sizable collection of timezone data files, which may present issues for packaging and deployment.
New and improved list operations. A new "lrepeat" command makes constructing large lists easy. Lsearch gets a new "-index" option for searching in nested lists (like lsort). Finally, lsearch, lsort, and switch get new "-nocase" options to do case-insensitive comparisons, which should remove a few "string tolower" commands from your programs.
Encodings for Tcl scripts. Both the "source" command and the "tclsh" and "wish" applications now allow specifying a script's encoding via a "-encoding" flag, as opposed to assuming the system encoding.
New "apply" command for anonymous (lambda) procedures. This will be a welcome addition for people who enjoy functional programming. But it's also a way to create commands on the fly that you can pass around and call, without needing to create new entries in the command table. If you often create new commands with dummy names, and then pass around the names, this may be a better solution.
Files, pipes and exec. Both the "open |" and "exec" commands get a new "2>@1" syntax, which allows you to merge and capture both stdout and stderr. Open also gets a new "b" access mode for opening binary files, which automatically does the equivalent of a "fconfigure -translation binary" for you. Finally, "file attributes" and "file copy" add support for several Mac OS X specific features.
Namespace improvements. For developers who make extensive uses of namespaces in creative ways (more common than perhaps it need be), there are new "namespace ensemble", "namespace unknown", and "namespace upvar" commands, that may simplify some of your existing namespace manipulations.
Modernized TEA build system. For developers who build Tcl extensions who were scared off by earlier, increasingly complex versions of the TEA package specification and tools, newer versions of TEA have removed many of the most annoying problems, so it's worth revisiting converting your extension to use TEA.
On the Tk side, far and away the most significant change is the inclusion of the "themed Tk" widgets. If you care at all about look and feel, this is one thing you definitely need to know about.
Alongside the classic Tk widgets, 8.5 introduces a new, complementary set of widgets, collectively referred to as "themed Tk" widgets. This is based on work done by Joe English, originally under the name "Tile".
Better platform look and feel. For developers working on mainstream Tk applications, the most important aspect of these themed widgets is that they deliver a look and feel more closely resembling the platform they are running on, as opposed to the often-dated appearance on some platforms (e.g. Linux) that many people have complained about. They also include some new widgets, e.g. combobox, that are common in today's user interfaces, but were only available as extensions in standard Tk.
As the name suggests, underlying the themed widget set is a mechanism allowing you to define new appearances via themes or styles, rather than via widget-by-widget customization or option database changes as is done with Tk. However, for most applications, the theming aspect will be secondary; the main value will be in the better appearance and new widgets.
How to use. The existing Tk widgets still remain, available through commands such as "button", but they also now are accessible via a "tk::" namespace, e.g. "tk::button". The themed widgets exist in a "ttk::" namespace, so there is now a "ttk::button" command.
Though the themed widget commands work similarly to the originals, there are important differences. Themed widgets are not a drop-in replacement. In particular, themed widgets generally provide less options for customizing their appearance than regular Tk widgets (e.g. they will often not have options like "-background"). Such changes, if needed (and they should be needed much less often) must be done by defining new widget styles, using the facilities offered by the themed widget package.
Complement, not replacement. Themed Tk widgets provide a better match for the "typical" uses of a widget, at the expense of the flexibility in many regular Tk widgets to turn themselves into something barely recognizable as the original. Because of this, applications will mostly want to use the themed widgets. However, to achieve particular effects, in special situations, it may make sense to use the regular Tk widget, if the flexibility it offers is all you need. One does not replace the other, and they can be mixed-and-matched as necessary within the same application.
Available ttk:: widgets. Alternatives to standard Tk widgets and containers: button, checkbutton, entry, label, menubutton, radiobutton, scale, scrollbar, frame, labelframe, panedwindow. New widgets not present in standard Tk: combobox, notebook, progressbar, separator, sizegrip. Note that these standard Tk widgets do not have (nor really need) an alternative in themed Tk: canvas, listbox, menu, spinbox, text.
The "widget" demo program that comes with Tk, though not currently fully updated to use Ttk everywhere, has added new demos that incorporate the new themed widgets. This is a good starting point to see how to use the new widgets and what they look like.
These themed widgets are the most significant upgrade to Tk in over ten years (since 8.0, the original port to Mac and Windows), and application developers should very seriously consider planning to migrate. This transition is non-trivial, because of the different approach to widget appearance that themed widgets take. The likely gains however are a better look and feel, removal of many existing hacks needed to make standard Tk widgets look better, and replacement of some extensions used to add widgets like notebooks. Widget builders should also examine the themed widget foundations, which provide a powerful way to construct new widgets. Because the themed widgets are still in rapid development, with new widgets planned, it's worth keeping an eye on future changes here.
Again, a few things that may well improve your programs, some without any changes needed on your part.
Antialiased text under X11, Mac OS X. This allows Tk to fit in much better with modern desktops; the lack of antialiased fonts has been a frequent criticism recently, particularly on Linux. Based on Xft on X11, ATSUI on OS X.
Mac Improvements. The Mac OS X version has received substantial improvements in look and feel of both the core and themed widgets, as well as many underlying optimizations, enhancements and modernizing.
Many text widget enhancements. These include smooth-scrolling (e.g. across tall images), new commands for counting characters, replacing text, handling display lines (not just logical lines), and more. See TIP #155 and TIP #169 for more.
Grid configure with widget name. It is now possible to pass the widget name to grid column/rowconfigure, whereas previously these accepted only a row/column index. This should make your grid layout code much more clear and obvious. It also simplifies things when adding new columns or rows after the fact, because you no longer have to adjust all the index numbers in your grid calls.
Tristate checkbutton and radiobuttons. Added a "-tristatevalue" that can be used to set a state where some but not all members of a collection of items are selected.
Full-Screen toplevels. A new "-fullscreen" option allows top-levels to be created which take up the entire screen, removing some of the pitfalls inherent in existing ad hoc methods of doing this.
Color window icons. A new "wm iconphoto" command allows specifying a color window icon, akin to the black and white icons you could use by "wm iconbitmap".
Enhanced font handling. There are now a wider variety of named fonts that you should use rather than specifying a particular font family, size, etc. to ensure that you get the right font for interface elements on each platform; see TIP #145.
Photo images catch memory errors. Photo images will now generate appropriate Tcl errors if enough memory is not available.
User-Data for virtual events. Virtual events can now specify a user-defined, event specific data value, useful for e.g. passing around an object or other additional information the event refers to. The "event generate" command now takes a "-data" option, while a "%d" in the event binding captures the data.
New panedwindow options. A new "-stretch" option allows more control over how different panes share the available space, while "-hide" is now available to control visibility.