Introduction to event loops
An event loop is a subroutine that periodically checks for events (mouse click, keyboard, etc.), and performs operations in response. Each operation should be short and self contained so that the event loop can quickly return to looking for the next event. Long operations result in the application becoming unresponsive while it completes the given operation, and then suddenly springing back into action, possibly doing a number of things quickly, as it catches up on the backlog of events. Most interactive applications use an event loop, including the browser you are reading this page through. A typical event is 'click mouse', which sends the location of the mouse click to a procedure which detects 'what was clicked' (a Tcl button, menu item or scale etc) and calls a command that is associated with the button (etc).
When you want a program that you interact with. Typically when you are using Tk or any other GUI library.
#! /bin/env tclsh proc count {} { variable count if {[incr count] > 10} { puts "I'm done!" set ::done 1 } else { puts $count after 1000 count } } after 0 count vwait done
Extending this example, let's have two processes feeding the event loop:
#! /bin/env tclsh proc count {} { variable count if {[incr count] > 10} { puts "I'm done!" set ::forever 1 } else { puts $count after 1000 count } } proc mood {} { variable count set mood [expr {$count % 2 ? "happy" : "sad"}] puts "I'm $mood!" after [expr {$count*500}] mood } after 0 count after 0 mood vwait forever
The [mood] command and the [count] command appear to be running concurrently. When using an event loop, it's best to think of the responses as "mini programs" which just happen to be running in the same interpreter. Since they can share the same global variables, event responses have to assume that other event reponses might have have changed the values of some global variables since the last time the first event response ran.
Unless you have been too clever for your own good, you have already got an event loop. See Tcl_DoOneEvent. Here is an example - copy the following and paste into a wish console:
#! /bin/env wish set i 0 proc evt {} { global i; incr i; puts "Event $i" } pack [button .a -text "Press Me" -command evt]
You will see a button labelled 'Press Me' appear in the second window (named wish84); press it and the console will display a message "Event 1"; again and "Event 2". Whenever you press the button, the procedure evt is invoked, i is incremented and its new value displayed. The event loop is busy waiting for you to press the Press Me button, or to receive more commands from the console. Now try this:
#! /bin/env wish set i 0 catch {destroy .a} {} ;# this destroys the old button proc evt {d} { global i; incr i $d; puts "Event $i"} pack [button .a -text "Press Me" -command "evt 1"] pack [button .b -text "Dont Press Me" -command "evt -1"]
you will see 2 buttons, press one and Event is incremented by 1; the other decrements. The event loop is getting your event and calling proc evt appropriately.
GWM For more exercise, type into the console:
bind .a <Enter> {puts "Entering press me"} bind .a <Leave> {puts "Leaving pressme"} bind .b <Enter> {puts "Entering dont pressme"} bind .b <Leave> {puts "Leaving dont pressme"}
Whenever your mouse cursor enters or leaves one of the buttons, an event is detected and calls the script in curlies. You have now a way of using the event loop to perform any Tcl proc.
What are some of the tricks in using an event loop? GWM make sure event checking is done regularly, at least every 0.1 seconds for good response. If you need a slow operation (read a large file for example) then either put up a 'wait....' type cursor or a '10-20-30% read display. Or break up the reading with Tcl_DoOneEvent() calls or run the slow operation in a separate thread.
What are some of the traps when programming an event loop? GWM Non- returning command issued from an event.