The event model is also used when programming user interfaces using Tk. Originally, event processing was only associated with Tk. The event loop moved from Tk to Tcl in the Tcl 7.5/Tk 4.1 release.
after 500During this time the application processes no events. You can use the vwait command as shown on page 180 to keep the Tcl event loop active during the waiting period.
The after command can register a Tcl command to occur after a period of time, in milliseconds:
after milliseconds cmd arg arg...The after command treats its arguments like eval; if you give it extra arguments it concatenates them to form a single command. If your argument structure is important, use list to build the command. The following example always works, no matter what the value of myvariable is:
after 500 [list puts $myvariable]The return value of after is an identifier for the registered command. You can cancel this command with the after cancel operation. You specify either the identifier returned from after, or the command string. In the latter case the event that matches the command string exactly is canceled.
Table 15-1 summarizes the after command:
The fileevent Command
The fileevent command registers a procedure that is called when an I/O channel is ready for read or write events. For example, you can open a pipeline or network socket for reading, and then process the data from the pipeline or socket using a command registered with fileevent. Using network sockets is described in Chapter 15. The advantage of this approach is that your application can do other things, like update the user interface, while waiting for data from the pipeline or socket. You can use fileevent on stdin and stdout, too.
The command registered with fileevent uses the regular Tcl commands to read or write data on the I/O channel. For example, if the pipeline generates line-oriented output, you should use gets to read a line of input. If you try and read more data than is available, your application may block waiting for more input. For this reason you should read one line in your fileevent handler, assuming the data is line-oriented. If you know the pipeline will generate data in fixed-sized blocks, then you can use the read command to read one block.
End of file makes a channel readable.
set pipe [open "|some command"]
fileevent $pipe readable [list Reader $pipe]
proc Reader { pipe } {
if [eof $pipe] {
catch {close $pipe}
return
}
gets $pipe line
# Process one line
}Table 15-2 summarizes the fileevent command.
fileevent fileId readable ?command? | Query or register command to be called when fileId is readable. |
fileevent fileId writable ?command? | Query or register command to be called when fileId is writable. |
The vwait Command
The vwait command waits until a variable is modified. For example, you can set variable x at a future time, and then wait for that variable to be set with vwait.
set x 0
after 500 {set x 1}
vwait xWaiting with vwait causes Tcl to enter the event loop. Tcl will process events until the variable x is modified. The vwait command completes when some Tcl code runs in response to an event and modifies the variable. In this case the event is a timer event, and the Tcl code is simply:
set x 1In some cases vwait is only used to start the event loop. The following example sets up a file event handler for stdin that will read and execute commands. Once this is set up, vwait is used to enter the event loop and process commands until the input channel is closed. The process exits at that point, so the vwait variable forever is not used:
fileevent stdin readable StdinRead
proc StdinRead {} {
global command prompt
if [eof stdin] {
exit
}
append command(line) [gets stdin]
if [info complete $command(line)] {
catch {uplevel #0 $command(line)} result
puts $result
puts -nonewline $prompt
flush stdout
set command(line) {}
}
}
puts -nonewline $prompt
flush stdout
vwait forever
fconfigure stdin-blocking 1 -buffering none -buffersize 4096 -eofchar {} -translation lf
Table 15-3 summarizes the properties controlled by fconfigure. They are discussed in more detail below.
Non-Blocking 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 OK 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.
fconfigure fileID -blocking 0It is not strictly necessary to put a channel into non-blocking 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 non-blocking. Perhaps the best motivation for a non-blocking channel is the buffering behavior of a non-blocking 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 non-blocking channel with fileevent. Example 15-3 shows a fileevent handler for a non-blocking channel. As described above, the gets may not find a complete line, in which case it doesn't read anything and returns -1.
set pipe [open "|some command"]
fileevent $pipe readable [list Reader $pipe]
fconfigure $pipe -blocking 0
proc Reader { pipe } {
if [eof $pipe] {
catch {close $pipe}
return
}
if {[gets $pipe line] < 0} {
# We blocked anyway because only part of a line
# was available for input
} else {
# Process one line
}
}
fconfigure fileID -buffering noneFull 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 8192Line buffering is used by default on stdin and stdout. Each newline in an output channel causes an write operation. Read buffering is the same as full buffering. The following command turns on line buffering:
fconfigure fileID -buffering line
The default behavior is almost always what you want, but you can control the translation with fconfigure. Table 15-4 shows settings for -translation:
End of File Character
In DOS file systems, there may be a Control-z character (\x1a) at the end of a text file. By default, this character is ignored on the Windows platform if it occurs at the end of the file, and this character is output when you close the file. You can turn this off by specifying an empty string for the end of file character:
fconfigure fileID -eofchar {}
set tty [open /dev/ttya]
fconfigure $tty -mode=> 9600,0,8,2
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. As of this writing the command is not implemented. Check your Tcl release for a serial command that will open serial devices on all platforms.
fconfigure pipe -translation {auto crlf} -buffersize 4096