Chapter 16. Event-Driven Programming

This chapter describes event-driven programming using timers and asynchronous I/O facilities. The after command causes Tcl commands to occur at a time in the future, and the fileevent command registers a command to occur in response to file input/output (I/O). Tcl commands discussed are: after, fblocked, fconfigure, fileevent, and vwait.

Event-driven programming is used in long-running programs like network servers and graphical user interfaces. This chapter introduces event-driven programming in Tcl. Tcl provides an easy model in which you register Tcl commands, and the system then calls those commands when a particular event occurs. The after command is used to execute Tcl commands at a later time, and the fileevent command is used to execute Tcl commands when the system is ready for I/O. The vwait command is used to wait for events. During the wait, Tcl automatically calls Tcl commands that are associated with different events.

The event model is also used when programming user interfaces using Tk. Originally, event processing was associated only with Tk. The event loop moved from Tk to Tcl in the Tcl 7.5/Tk 4.1 release.

The Tcl Event Loop

An event loop is built into Tcl, which checks for events and calls out to handlers that have been registered for different types of events. Some of the events are processed internally to Tcl. You can register Tcl commands to be called in response to events. There are also C APIs for the event loop, which are described on page 781. Event processing is active all the time in Tk applications. If you do not use Tk, you can start the event loop with the vwait command as shown in Example 16-2 on page 230. The four event classes are handled in the following order:

  • Window eventsThese include keystrokes and button clicks. Handlers are set up for these automatically by the Tk widgets, and you can register window event handlers with the bind command described in Chapter 29.

  • File and socket I/O eventsThe fileevent command registers handlers for these events.

  • Timer eventsThe after command registers commands to occur at specific times.

  • Idle eventsThese events are processed when there is nothing else to do. The Tk widgets use idle events to display themselves. The after idle command registers a command to run at the next idle time.

The after Command

The after command sets up commands to happen in the future. In its simplest form, it pauses the application for a specified time, in milliseconds. The example below waits for half a second:

after 500

During this time, the application does not process events. You can use the vwait command as shown on page 230 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 16-1 summarizes the after command:

Table 16-1. The after command

after milliseconds

Pauses for milliseconds.

after ms arg ?arg...?

Concatenates the args into a command and executes it after ms milliseconds. Immediately returns an ID.

after cancel id

Cancels the command registered under id.

after cancel command

Cancels the registered command.

after idle command

Runs command at the next idle moment.

after info ?id?

Returns a list of IDs for outstanding after events, or the command associated with id.

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. 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. Network servers use fileevent to manage connections to many clients. You can use fileevent on stdin and stdout, too. Using network sockets is described in Chapter 17.

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.

The fconfigure command, which is described on page 232, can put a channel into nonblocking mode. This is not strictly necessary when using fileevent. The pros and cons of nonblocking I/O are discussed later.

Note

The fileevent Command

End of file makes a channel readable.

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 24-1 on page 378 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.

Table 16-2. The fileevent command

fileevent fileId readable ?command?

Queries or registers command to be called when fileId is readable.

fileevent fileId writable ?command?

Queries or registers 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 x

Waiting 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 1

In some cases vwait is used only to start the event loop. Example 16-2 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 Stdin(wait) is not used:

Example 16-2. Using vwait to activate the event loop

proc Stdin_Start {prompt} {
   global Stdin
   set Stdin(line) ""
   puts -nonewline $prompt
   flush stdout
   fileevent stdin readable [list StdinRead $prompt]
   vwait Stdin(wait)
}
proc StdinRead {prompt} {
   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) 

   }
}

The fconfigure Command

The fconfigure command sets and queries several properties of I/O channels. The default settings for channels are suitable for most cases. If you do event-driven I/O you may want to set your channel into nonblocking mode. If you handle binary data, you should turn off end of line and character set translations. You can query the channel parameters like this:

fconfigure stdin
=> -blocking 1 -buffering none -buffersize 4096 -encoding iso8859-1 -eofchar {} 
The fconfigure Command-translation lf

Table 16-3 summarizes the properties controlled by fconfigure, not including properties for serial lines.

Table 16-3. I/O channel properties controlled by fconfigure

-blocking

Blocks until I/O channel is ready0 or 1.

-buffering

Buffer modenone, line, or full.

-buffersize

Number of characters in the buffer.

-encoding

The character set encoding.

-eofchar

Special end of file character. Control-z (x1a) for DOS. Null otherwise.

-lasterror

Returns the last POSIX error message associated with a channel.

-translation

End of line translationauto, lf, cr, crlf, binary.

-peername

Sockets only. IP address of remote host.

-peerport

Sockets only. Port number of remote host.

Serial lines have many additional properties. Before Tcl 8.4, you could only control the baud rate, parity and number of bits using the -mode property. Many new properties for serial line control were added in Tcl 8.4. Table 16-4 lists the serial line properties set by fconfigure.

Table 16-4. Serial line properties controlled by fconfigure

-mode

Formatbaud,parity,data,stop.

-queue

Returns a list of two integers representing the current number of bytes in the input and output queues. Tcl 8.4.

-timeout

Specifies the timeout in milliseconds for blocking reads. Tcl 8.4.

-ttycontrol

Sets up the handshake output lines. Tcl 8.4.

-ttystatus

Returns the current serial line status. Tcl 8.4.

-xchar

Specifies the software handshake characters. Tcl 8.4.

-handshake

Specifies one of rtscts, xonxoff or (Windows only) dtrdsr. Tcl 8.4.

-pollinterval

Sets the maximum time for polling of fileevents (Windows only.) Tcl 8.4.

-sysbuffer

Specifies the size of system buffers for a serial channel. (Windows only.) Tcl 8.4.

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

The fblocked Command

The fblocked command returns 1 if a channel does not have data ready. Normally the fileevent command takes care of waiting for data, so I have seen fblocked useful only in testing channel implementations.

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

End of Line Translations

On UNIX, text lines end with a newline character ( ). On Macintosh they end with a carriage return ( ). On Windows they end with a carriage return, newline sequence ( ). Network sockets also use the carriage return, newline sequence. By default, Tcl accepts any of these, and the line terminator can even change within a channel. All of these different conventions are converted to the UNIX style so that once read, text lines always end with a newline character ( ). Both the read and gets commands do this conversion. By default, text lines are generated in the platform-native format during output.

The default behavior is almost always what you want, but you can control the translation with fconfigure. Table 16-5 shows settings for -translation:

Table 16-5. End of line translation modes

binary

No translation at all.

lf

UNIX-style, which also means no translations.

cr

Macintosh style. On input, carriage returns are converted to newlines. On output, newlines are converted to carriage returns.

crlf

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.

auto

The default behavior. On input, all end of line conventions are converted to a newline. Output is in native format.

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 {}

In Tcl 8.4 the end-of-file character trick is used by Tcl_EvalFile and source to allow Tclkit and other tools to append non-script data to script files. This is enabled by default, and should not normally interfere with your scripts.

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

Tcl 8.4 added the enhanced control of serial channels for Windows and Unix systems. The options are listed in Table 16-4.

Windows has some special device names that always connect you to the serial line devices when you use open. They are com1 through com9. To access com devices above 9, use this form: {\.comXX}. The Windows system console is named con. The Windows 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:

Character Set Encodings

Tcl automatically converts various character set encodings into Unicode internally. It cannot automatically detect the encoding for a file or network socket, however, so you need to use fconfigure -encoding if you are reading data that is not in the system's default encoding. Character set issues are explained in more detail in Chapter 15.

Configuring Read-Write Channels

If you have a channel that is used for both input and output, you can set the channel parameters independently for input and output. In this case, you can specify a two-element list for the parameter value. The first element is for the input side of the channel, and the second element is for the output side of the channel. If you specify only a single element, it applies to both input and output. For example, the following command forces output end of line translations to be crlf mode, leaves the input channel on automatic, and sets the buffer size for both input and output:

fconfigure pipe -translation {auto crlf} -buffersize 4096
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.22.136