Common Lisp/External libraries/Ltk

Ltk is a portable set of Tk bindings for Common Lisp. Tk is a graphical ‘toolkit’, a library that makes GUI building easier by providing widgets, i.e. graphical elements such as buttons an drop-down menus. Tk originated and gained great popularity as part of Tcl/Tk, the dynamic Tcl language paired with the simple to use Tk toolkit. Due to Tcl/Tk's popularity, implementations exist for most systems and Ltk is portable to any such system.

Ltk is not truly a binding in the usual sense, it is really an interface to the wish shell, a shell that interprets commands to build a window. This allows Ltk to operate via a simple stream of commands to an external process running wish, rather than the linking needed in most other GUI builders. While passing commands to the wish interpreter may produce slower programs, it makes the Ltk package very simple to build, use, extend, and debug, and it removes the need for any FFI. These features stand in stark contrast to most other GUI builders for Common Lisp.

Basic Ltk
A program that uses Ltk to build its GUI needs three main parts. It needs to create widgets, place the widgets on the window, and then associate actions for when the widgets are activated. An Ltk program typically has the following basic format:

Creating Widgets
Ltk operation is based on the instantiation of CLOS objects. To create any particular widget, you would make and instance of that class. This instance does not need to be retained, but if it is, it can then be used to alter the properties of the widget.

Ltk includes the following common widgets:


 * Buttons
 * Check boxes
 * Radio buttons
 * Text entry fields and text editor boxes
 * Listboxes
 * Menu bars
 * Frames and scrolled frames
 * Canvases and scrolled canvases

If this list seems quite short, you are not wrong. These widgets will allow you to create many useful programs, but you will find them lacking for certain tasks (such as a progress indicator for a download). Latter we will see how to add new widgets.

Laying out Widgets
With Ltk (as with Tk) once a widget is created, it is not visible until you tell wish where to put it in the window. Ltk has two geometry mangers, pack and grid. Pack treats widgets as boxes and packs them horizontally or vertically. Grid lays out widgets on a regular grid. Of the two, <tt>pack</tt> is much more commonly used.

Events and Binding
Like most graphical interfaces, all computation in Ltk is event driven in nature. This means that any event that happens inside your window, like a key press or a mouse click, is sent to your program by the windowing system. Ltk then looks up what handler functions, typically defined by you, are associated with this event and calls them. Event handlers can be established in two ways, by the <tt>command</tt> slot in many widgets and <tt>bind</tt> function. The <tt>bind</tt> function is more complex than the <tt>command</tt> slot method, but <tt>bind</tt> is a general and more flexible way to associate functions with events.

Placing Widgets
For widgets to be visible, they must be placed on the window. This is done with one of three functions: ‘<tt>place</tt>’, ‘<tt>pack</tt>’, or ‘<tt>grid</tt>’. Each is used for different circumstances. In some simple cases, widgets may be placed by specifying the placement method as a keyword argument to <tt>make-instance</tt>. We will deal with each of these methods in turn.

Placing Widgets
The most rudimentary way of placing widgets on a window is to specify an x and y position along with a width and height. The ‘<tt>place</tt>’ function will perform this task for you. However, this is typically harder than it needs to be (compared to the methods we will explore next) so its use is not advised for any purpose.

Grid Geometry
Here is a mock up of the video game Lights Out. We use the <tt>grid</tt> geometry manager to assemble an NxM array of buttons where each button is either ‘on’ or ‘off’. For simplicity sake we will represent on by an ‘X’ and off by no text. Whenever you click a button, it toggles the button and the four adjacent buttons between the blank and ‘X’ state. The goal is to change all of the buttons to blanks.



Modifying Widgets with ‘<tt>configure</tt>’
The function <tt>configure</tt> is used to change various attributes of a widget such as color and font.

Binding
Binding, in the Tk sense, is associating actions with events. We do this by calling the function <tt>bind</tt> which modifies a widgets list of event handlers.

The Canvas Widget
The canvas widget allows you to draw arbitrary vector graphics.

Modifying canvas items

 * ‘<tt>itemconfigure</tt>’
 * ‘<tt>itemmove</tt>’
 * ‘<tt>itembind</tt>’
 * ‘<tt>itemdelete</tt>’
 * ‘<tt>itemlower</tt>’ and ‘<tt>itemraise</tt>’

Bitmaps
Ltk can display bitmaps in widgets. By default, Tk (and thus Ltk) supports <tt>GIF</tt>, <tt>PBM</tt>, and <tt>PPM</tt> images, all of which are rarely used these days. In order to use modern formats like <tt>PNG</tt> and <tt>JPEG</tt> you will need to install the <tt>libimg</tt> extension library for Tcl/Tk (e.g. in Ubuntu, you need to install the <tt>libtk-img</tt> package) and require the package before you use it (see the code below).

The following piece of code creates an Ltk window with a canvas object in it. It then loads the image specified by the pathname <tt>filename</tt> and places it on the canvas. Anything outside of the 300x300 pixel canvas is merely not drawn.

The return value of <tt>create-image</tt> is a canvas item designator. It could be saved so the image may be modified later (i.e. moved around like in the canvas item modification examples).

Bitmaps are not limited to canvas widgets. They can also be placed on buttons instead of text.

Threads, lengthy calculations and ‘<tt>process-events</tt>’
The basic structure of an event driven system might look something like the following:

This is called an event loop, or event pump. The purpose of the event loop is to map the actions that a user performs onto the functions defined to handle them. Any action the user makes causes new event to be inserted in a queue. The function <tt>get-next-event</tt> pulls the next event out of the queue and the case statement dispatches it to the correct location. If a handler doesn't return in a timely manner, this can lead to an unresponsive program.

In order to use Ltk for more applications, we need methods of avoiding this unresponsive behavior, so we can do things like have cancel button to stop a lengthy calculation. There are several methods of dealing with this issue; we will discuss three:


 * 1) Set up a timer event in the Tk event loop.
 * 2) Periodically pause your calculations to explicitly process events via <tt>process-events</tt>.
 * 3) Use the Lisp implementations internal threads to separate computations from the thread running the event loop.  Of course this only works on threaded Lisp implementations.

Timers and ‘<tt>after</tt>’
In some cases, the handler is slow to return because you are waiting for something to happen, not computing at all. In these cases, the correct way for the handler to behave is to return immediately, but not before telling the event loop to retry at some later time. This is done by inserting a timer event which will come off the queue after your specified delay. A timer event is set up by using the <tt>after</tt> function.

‘<tt>process-events</tt>’ and ‘<tt>after-idle</tt>’
In order to use <tt>after</tt>, it was necessary that our task was only waiting (in our example above, waiting for input on a socket) and easily suspended and resumed. If this is not the case, then you have another option available, <tt>process-events</tt>. With <tt>process-event</tt>, instead of inserting a new event and allowing LTK to process other event in the mean time, it asks the main loop to process all events currently waiting in the queue then return and allow your code to continue.

Similarly, <tt>after-idle</tt> behaves like <tt>after</tt> except it gets processed once the event queue is flushed but no intrinsic delay. This has the basic behavior of <tt>process-events</tt> but instead of resuming in the middle of a function, it triggers a new function call. It is useful if your code is not just waiting, but is easily suspended and resumed.

Threaded Ltk applications
Using threads is by far the most powerful solution to this problem. However, with that power, there is a great deal of added complexity. All of the issues with threaded programs come up. There is an added catch with Ltk, though. The <tt>with-ltk</tt> form sets up a special environment for your Ltk commands. However, dynamic variables by design are not lexically captured. This means that your function that is bound to an event is not necessarily executed in an environment where the dynamic Ltk environment is set up (although it seems like it should be). Although this comes up in single threaded Ltk programs as well, this happens all the time in multithreaded programs. The solution is to force the function to lexically close over the desired environment and remake the environment inside the function.

Remote Ltk sessions
Tk (and hence Ltk) provides for remote sessions. In a remote session, a program is run on a server but the display happens elsewhere, on a client computer. The display information is transmitted as a series of <tt>wish</tt> commands, so efficiency of such a system is not bad. To X11 users, this is quite similar to X11 forwarding.