Introduction

[This is Work In Progress currently. My apologies.]

Here we will discuss the most important parts of the cells-gtk GTK binding, and how cells can be used in defining cells-gtk GUIs. This minimal introduction might be sufficient to get you started. We'll see.

Cells, Cell slots, and all that...

The example from Bill Clementson's Blog is a nice starting point. Read it and come back here.

...Welcome back. Perhaps you came back with the impression that Cells is a way to manage the relationship among the values in the slots of a CLOS object. That would be good. You might also imagine that if the slots describe the state of some real world object like a motor, you could use Cells to control the object -- to orchestrate how all its parts work together toward some goal. That also would be good. Now, if that object were a GTK-based GUI... no I'm getting ahead of myself. Let's look at the basic idea of Cells, it is similar to constraint satisfaction. That is, you have values, and rules that govern the relationships among the values, and whenever the network is pushed out of a consistent state (i.e. whenever the rules no longer hold), the objective is to get it back to a consistent one by updating some values. In Bill's example, status fuelpump and temp are cells -- slots in a motor CLOS object whose values are managed by the cells constraint network. The constraint network itself might be depicted as this:

[graph]

In this graph the nodes are values and edges have rules attached. The collection of edges into a node together with the rule are the cell. The green nodes are values controlled by cells and the empty nodes are regular 'unmanaged' values (such as you get by reading the value from regular CLOS slot).

Cells such as temp, which have no edges leading into them, are called 'c-input cells'. A c-input cell gives you an opportunity to push the network out of a consistent state, so that it must find another consistent state (raise the temperature, force the motor and fuel pump to stop). They are one way the network interacts with its environment. (Thinking about GUI: Part of the 'environment' of your Cells-GTK application are lisp objects, the state of which you'd like to communicate to the user.)

Another way that the cells network can interact with its environment is through c-output (or c-echo) methods. These are 'observers' of a cell that are invoked when the value of that cell is modified. They are sort of the converse of the c-input: modify a c-input value and the network reacts; when the network reacts, it can modify values outside the network with a c-output.

So what about the values inside the network -- the nodes with edges pointing into them? Observing those with c-output methods and using c-input and setf to modify them would be bad form. If you program that way, you aren't doing anything that couldn't be done without Cells. Drop Cells-GTK and go back to programming in C-based GTK or Java. The result will be that you hand back to the programmer the burden of keeping track of every change to the GUI. C-input cells are intended (small apps like Bill's aside) to be used to 'instrument' (or "put sensors on") your code. If you have a legacy code that you are building a Cells-GTK GUI for, ....

Now reiterating the ideas above:

Cells provides for the declaration of several kinds of "cell slots," but for the purpose of talking about cells-gtk, we can limit the discussion to just the c-input and c-formula cells. How do you recognize what kind of cell slot is being used? Well, first we should point out that by specifying :cell nil in a defmodel slot definition, the slot defined is an ordinary CLOS slot. The rest are specified using :initform on the slot. The following defmodel defines a c-input cell, a c-formula cell and a regular CLOS slot.
(defmodel example ()
  ((control-me :initform (c-input (some-form-foo)) :accessor control-me)
   (im-computed :initform (c-formula (some-form-bar)) :accessor im-computed)
   (im-clos :cell nil :initform nil)))

         slot
        /    \
     CLOS   cell
            /   \
     invariant  variant
                 /    \
      input-valued   formula-valued
The figure above depicts a taxonomy of cell slots. Cell slots can be categorized as invariant or variant. Variant slots can be further categorized as input-valued and formula-valued. Some definitions:

Cells and Cells-GTK

Good Cells Technique

This all may seem abstract, but it has practical value in the design of your GUI. For example, I might set the :sensitivity of a button, the :text in a label or :fraction of a progress-bar based on the presence or value of an object in another cell slot, or some relationship of cells slots described by the formula. Thus the problem of remembering "what to set when" is reduced to answering the question, "What does the state of the widget presented to the user represent about the state of the program?" The formula addresses that question directly, and is placed at the widget presenting itself.

A few notes

Cell Input, Output, and Formula-valued Cells

The above described the purpose of the input cell. There is often the complementary need to react to the change of a value. def-c-output defines a method named for a cell slot and specialied on a defmodel defining the slot (or one of its subclasses, the usual CLOS rules apply).

The def-c-output method is invoked through process of propagation of an change to an input as follows:

Each SETF gets process in order by starting from the top.

The above happens with the following guarantee: When an input changes, all dependencies (direct or indirect) are considered "stale" until they are recomputed. The guarantee ensures that no cell slot accessor will ever return a "stale" value.

Note that concerning outputs setf-ing inputs, each SETF is considered a "discrete generation" if you will. This is why all propagation to formula-valued cell dependencies has to happen before any output SETF (and why Cells has a mechanism to defer SETFs of inputs).

Family: Referencing cells throughout the defmodel hierarchy.

The defmodel macro defines a part-subpart relationship among components of the interface. The relationship is made by the :kids initarg. :kids is given a list of children. In a cells-gtk defmodel some kids might be GTKContainer (vbox, hbox, etc) -- things that in the GTK world, have kids of their own. It may be a natural coding practice in that case for the defmodel to define a fairly deep ancestory hierarchy, laying out the arrangement of widgets. (You're free to break things up wherever seem reasonable for your application). It might look like this:
(defmodel my-app (gtk-app)
  ()
  (:default-initargs
  :md-name :my-app
  :kids 
  (list 
    (mk-vbox
      :kids
        (list 
           (mk-hbox
	      :kids
                 (list (mk-combo-box :md-name :my-combo) ...))
           (mk-hbox
	       :kids
                (list (make-instance 'my-subpart))))))))
The point of the Family methods is to allow things a different places in this hierarchy (e.g. :my-combo to reference :my-app).

More will be written about this soon.


Peter Denno
Last modified: Sun Mar 6 18:43:09 EST 2005