Event tutorial

MJ - The following is a work in progress. I couldn't find a good event tutorial starting from basics anywhere. If this exists, please include a link.

A Tcl event tutorial

The goal of this page is to give an introduction to the Tcl event loop suitable for Tcl beginners. If you make changes to this page, please keep that in mind.

Assumptions: it is assumed that you are reasonably versed in Tcl. This means that you should know:

  • How Tcl handles quoting and grouping. You should know the difference between $var, "$var", {$var} and [$var]. If you don't know this difference, read the page from man -s n Tcl, exec quotes problem, Quoting Hell, quoting heaven
  • How to create procedures (see man -s n proc)
  • How Tcl handles global variables (see man -s n variable and man -s n global)
  • Basic concepts of programming in Tcl

If you don't know these things, it would be better to start with a generic Tcl tutorial first tcltutorial.

What is event based programming

  • What is an event?
  • Why would you use/need events?
  • What are common terms relating to events - queues, handler, bind, event loop, dispatcher

When you write an console based application, you don't usually use events. The commands in your script will be executed in a specific order. For example the script:

 set a 1
 set b 2
 puts $a
 puts $b

will:

  • set a to 1
  • set b to 2
  • show the value of a
  • show the value of b

For a large class of applications, this is not a limitation and you can get a long way without ever needing events.

Now imagine you write an application that takes user input and, based on that user input, takes a certain action. For instance you have a very simple application that add two numbers - a and b - that are entered by the user. One way to write this is like:

 proc sum {} {
        puts -nonewline "a: "
        flush stdout
        gets stdin a
        puts -nonewline "b: "
        flush stdout
        gets stdin b
        set result [expr {$a + $b}]
        puts "$a + $b = $result"
 }

 sum

With a typical output like:

 a: 12
 b: 412
 12 + 412 = 424

Note that in this example, the order of events (user input, program output) is very well defined. This also means the order of events is very inflexible. If the user inputs an incorrect value for a, he will have to stop the program and restart. There is no way to set the value of a to something else.

So if you want to allow the user to take actions in a more random order, this will be fairly limited. It would be nice, for instance, if after calculating the sum for a = 133 and b = 134, the user could change the value of b and calculate the sum again without having to reenter a. This would lead to something more like:

  set a 0
  set b 0
 
  proc showvalues {} {
       global a b
       puts "a: $::a, b: $b"
  }
 
  proc showoptions {} {
      puts "Valid commands are:"
      puts "a - set the value for a"
      puts "b - set the value for b"
      puts "= - calculate the sum"
      puts "q - quit"
  }
 
  puts "Welcome to sum."
  puts --------
  showoptions
  puts --------
  showvalues
 
 
  while 1 {
        puts -nonewline "Command: " 
        flush stdout
        gets stdin input
        switch $input {
              a      {puts -nonewline "new value for a: " ; flush stdout ; gets stdin a ; showvalues}
              b      {puts -nonewline "new value for b: " ; flush stdout ; gets stdin b ; showvalues}
              =      {showvalues ; puts "$a + $b = [expr {$a + $b}]"}
              q      {exit}
              default {showoptions}
        }
  }

Although this program is quite a bit longer than the first try, we have created a lot of flexibility. The user is now pretty much free to take his own path through the application instead of having to follow the predefined steps as in the earlier example.

The major part of the program is implemented in the while loop. This shows the beginnings of a rudimentary event loop. The system is waiting for an event (the user presses a key) and takes an appropriate action (execute the correct switch case). To make the correspondance even clearer:

In this example, the while acts as the event loop, the gets generates the event, and the switch handles the event.

If you forget about all the details and complexity, this is the essence of how event-based programming works.

  • The program enters an event loop, waiting for events to occur (while loop)
  • An event occurs (user enters a command)
  • The event is dispatched (switch statement)
  • The event is handled (cases in the switch statement)
  • The process continues

The only concepts in event-based programming that are not covered in the example are event queues and binding of event handlers to events. There will be more written about those later. In the second example, we have created our own event loop, dispatcher and event handlers. When you use event based programming in Tcl you will not do this yourself, because Tcl already includes an event loop.

When you start building GUIs, it is even more essential that you will need to act on events, because the user has a very large number of possible actions she can perform at any given moment in time. Think about:

  • Pressing a button
  • Opening a menu
  • Typing some text in a text box

If all of these actions generate events and we have some way of reacting to these events we can take the correct action based on what the user does without knowing when he will do it (e.g. she might enter some text first and then press a button or vice versa).

How does Tcl use event based programming

  • file events
  • eventloop
  • callbacks
  • after
  • event sources

Events in Tk

Common pitfalls when using events in Tcl

  • event callbacks execute in global scope
  • events should not take long to handle
  • Difference in time between callback definition and execution, leading to
  • callback quoting hell -> use procs + list

Where to go from here

Links to event related man pages

See also after,