Lyntin

tutorials: using hooks

documentation> tutorial 2: using hooks

last updated: Sunday, August 20th

Summary

Lyntin's framework relies heavily on the use of hooks to let modules participate in the handling of events and in the transformation of internal data. This tutorial walks through the basics of using a hook. It's basic in the sense that we're going to talk about a semi-trivial module that doesn't do a whole lot in order to clearly show how hooks work and how to work with them. This tutorial assumes that you have a working knowledge of Python and that you've read the first Lyntin tutorial.

Revisions

date description who
1/25/2003 wrote the document based on Lyntin 3.1 wbg
4/13/2003 moved it out of the LCR and into the documentation section wbg

Step 1: defining the problem we're trying to solve

In the first tutorial, I wrote a basic status bar module that allows me to track name/value pairs that are generated elsewhere--probably by actions. In this tutorial, I'm going to add to that the ability to figure out whether I'm idle and when I am idle, it'll tell me how many minutes I've been idle for in the statusbar using the #setstatus command we wrote in the first tutorial.

Step 2: building the module

I name my module idler.py and I place it in my personal modules directory.

I open my favorite editor and type the following (or copy and paste it from previously existing modules):

import exported

# this holds the Lyntin tick value of the last time I (as a user)
# executed a Lyntin command
lyntin_tick = 0

# lets us keep track if we're idling longer than 60 seconds
idle_flag = 0

# the current tick
current_tick = 0

def handle_timer(args):
   global idle_flag, lyntin_tick, current_tick

   current_tick = args[0]
   delta = current_tick - lyntin_tick
   if delta > 60:
      idle_flag = 1
      exported.lyntin_command("#setstatus idle %d" % delta, internal=1)

def handle_from_user(args):
   global idle_flag, lyntin_tick, current_tick

   if idle_flag == 1:
      exported.lyntin_command("#setstatus idle", internal=1)
      idle_flag = 0

   lyntin_tick = current_tick

def load():
   """ Initializes the module by binding all the commands."""
   exported.hook_register("timer_hook", handle_timer)
   exported.hook_register("from_user_hook", handle_from_user)

def unload():
   """ Unbinds the commands (for when we reimport the module)."""
   exported.hook_unregister("timer_hook", handle_timer)
   exported.hook_unregister("from_user_hook", handle_from_user)

That's my basic module shell. Let's walk through the important pieces.

The first important piece is:

import exported

We import the exported module which is our interface into Lyntin's internals. We're planning to use the statusbar module to update our title bar, but we're actually going to use it by sending commands to Lyntin rather than executing functions directly.

# this holds the Lyntin tick value of the last time I (as a user)
# executed a Lyntin command
lyntin_tick = 0

# lets us keep track if we're idling longer than 60 seconds
idle_flag = 0

# the current tick
current_tick = 0

Here we're defining the variables we're going to need to track the current tick, the tick the user last entered a command on, and whether we're in idle mode or not.

The next important piece:

def handle_timer(args):
   global idle_flag, lyntin_tick, current_tick

   current_tick = args[0]
   delta = current_tick - lyntin_tick
   if delta > 60:
      idle_flag = 1
      exported.lyntin_command("#setstatus idle %d" % delta, internal=1)

Here we define the function that's going to be registered with the timer_hook. The section in hooks.py on the timer_hook, has the following:

# The timer hook runs every second.  The tickers for the various sessions
# use this hook to figure out when to tick.
# 
# arg tuple: (int)
#  - the current tick since Lyntin started

In engine.py is the code for the thread whose sole purpose in life is to sleep for a second and then generate a SpamEvent to kick off the timer_hook with the current tick. When the SpamEvent is executed, all the functions registered with the timer_hook get executed. It passes to them a tuple with only one argument which is the current tick. The current tick is the number of seconds since Lyntin started.

In our module, we want to capture the current tick in our current_tick global variable. Then we want to see if it's been at least 60 seconds since the last time the user entered in some data. If it has, then we update the statusbar using the #setstatus command we wrote in the first tutorial. We also update our idle_flag to 1 indicating that we're now idling.

The next section:

def handle_from_user(args):
   global idle_flag, lyntin_tick, current_tick

   if idle_flag == 1:
      exported.lyntin_command("#setstatus idle", internal=1)
      idle_flag = 0

   lyntin_tick = current_tick

This function is registered with the from_user_hook. Looking at the comments in hooks.py in regards to the from_user_hook we find the following:

# Everything the user types gets sent on the from_user_hook.
#
# arg tuple: (string)
#  - the data the user just entered

This hook tells us when the user has sent a command to Lyntin (by pressing the carriage return key) as well as what they entered. In this case, we don't really care what they typed, but we do care when they typed it. That's why in the handle_timer function above we capture the current tick. When the user enters something, we can update the lyntin_tick global variable with the current_tick.

In addition to that, if we're in idle mode, we want to change our flag to 0 and reset our statusbar. So we do so accordingly.

The last section:

def load():
   """ Initializes the module by binding all the commands."""
   exported.hook_register("timer_hook", handle_timer)
   exported.hook_register("from_user_hook", handle_from_user)

def unload():
   """ Unbinds the commands (for when we reimport the module)."""
   exported.hook_unregister("timer_hook", handle_timer)
   exported.hook_unregister("from_user_hook", handle_from_user)

This is where we register and unregister with the hooks we're interested in.

That's it for the code in the module.

Step 3: adding functionality

Let's test it out by starting up Lyntin and idling for 60 seconds.

testing our module
image 1: testing our module

Woah Nelly! I'm not sure if I want a second-by-second breakdown of how long I've been idle for. I'm idle--there's no need to rub it in with large numbers. So we're going to modify the handle_timer function a bit.

def handle_timer(args):
   global idle_flag, lyntin_tick, current_tick

   current_tick = args[0]
   delta = current_tick - lyntin_tick
   if delta > 60 and idle_flag == 0:
      idle_flag = 1

   if delta % 60 == 0:
      exported.lyntin_command("#setstatus idle %dmin" % (delta / 60), internal=1)

Now it will only update every minute and it tells me how many minutes I've been idle for.

our module with idle minutes
image 2: our module with idle minutes

When we hit the enter key, it clears the status variable:

cleared title bar
image 3: cleared title bar

Step 4: finishing it up

Now that you have a nice idler module, you might want to consider getting it added to the Lyntin code repository. Instructions for that are at http://lyntin.sourceforge.net/repository.php. Add a module doc-string explaining what the module does and what deficiencies it has. Add some module attributes about who wrote it, when, and the version number.

Step 5: viewing the complete module

"""
This modoule keeps track of whether I'm idle or not and lets me
know how many minutes I've been idle using the #setstatus.
"""
__author__ = "Will Guaraldi"
__version__ = "1.0"
__date__ = "January 25, 2003"

import exported

# this holds the Lyntin tick value of the last time I (as a user)
# executed a Lyntin command
lyntin_tick = 0

# lets us keep track if we're idling longer than 60 seconds
idle_flag = 0

# the current tick
current_tick = 0

def handle_timer(args):
   global idle_flag, lyntin_tick, current_tick

   current_tick = args[0]
   delta = current_tick - lyntin_tick
   if delta > 60 and idle_flag == 0:
      idle_flag = 1

   if delta % 60 == 0:
      exported.lyntin_command("#setstatus idle %dmin" % (delta / 60), internal=1)

def handle_from_user(args):
   global idle_flag, lyntin_tick, current_tick

   if idle_flag == 1:
      exported.lyntin_command("#setstatus idle", internal=1)
      idle_flag = 0

   lyntin_tick = current_tick

def load():
   """ Initializes the module by binding all the commands."""
   exported.hook_register("timer_hook", handle_timer)
   exported.hook_register("from_user_hook", handle_from_user)

def unload():
   """ Unbinds the commands (for when we reimport the module)."""
   exported.hook_unregister("timer_hook", handle_timer)
   exported.hook_unregister("from_user_hook", handle_from_user)

Rock on!