[cells-devel] Something like a def-family-observer?

Ken Tilton kennytilton at optonline.net
Wed Dec 12 01:39:50 UTC 2007


Peter Hildebrandt wrote:
> 
> Ken,
> 
> thanks for your reply, and sorry about the confusion.  I guess I have 
> been  working on this for too long :)
> 
> So, here's a second try.  I'll try to write some code to make clear 
> what  this is about, and what I want to do.
> 
> First, here's what we have:
> 
> My family tree:
> 
> (defmodel node (family) (age :accessor age :initarg :age :initform 
> (c-in  17)))
> (defparameter root (make-instance 'node :md-name "root"))
> (push (make-instance 'node :md-name "child1" :age 23) (kids root))
> (push (make-instance 'node :md-name "child2" :age 32) (kids root))
> (push (make-instance 'node :md-name "grand child" :age 3) (kids (first  
> (kids root))))
> 
> A tree-view widget (note that the tree view widget displays something  
> called a "tree model".  That is a data structure somewhere in GTK that 
> can  be build and accessed through a bunch of messy gtk function calls.  
> So  what we have to do is go through our family and build the tree 
> model  accordingly.  Our func is kinda like a fax that makes a copy and 
> sends it  out to the recipient.  If the original changes, we have to 
> send the fax  again.)
> 
> (defmodel tree-view (family)
>   ((roots :accessor roots :initform (c-in nil) :initarg :roots)
>   ;; other slots, GUI stuff
>   ))
> 
> (def-c-output roots ((self tree-view))
>   ;; cells-gtk has lots of calls to GTK here
>   ;; to build a tree model starting from roots (see above)
>   (print (list "roots have changed" new-value)))
> 
> (defparameter tview (make-instance 'tree-view))
> 
> I connect it to the kids of the root
> 
> (setf (roots tview) (kids root))
>   =>  "roots have changed ...."
> And there it is:
> 
> md-name           age
> ---------------------
> child2            32
>  +-grand child     3
> child1            23
> 
> So far, it is all there in cells-gtk, and it all works.
> 
> However, if my family tree /changes/, meaning
> - somewhere a node is added/removed
> - somewhere a slot is modified
> 
> ==> I'd like the treeview to learn about it.
> 
> How do we do this?
> 
> (setf (md-name (first (kids root))) "My child")
>   => "My child"
> 
> (push (make-instance 'node :md-name "child3") (kids root))
>   => (child3 child2 child1)
> 
> ... but the def-c-output never finds out about it, and thus no one ever  
> gets round to up.
> 
> I can trigger it by re-setfing the place:
> 
> (setf (roots tview) (kids root))
>   => ("roots have changed" (child3 child2 child1))
>   => (child3 child2 child1)
> 
> But that has three downsides:
> 
>   (1) I have to call setf manually (even when I disguise it with some  
> macro as (update-tree tview))
>   (2) what do I do, when roots is a dependent cell in the first place? 
> ...  :roots (c? (kids root))
>   (3) the whole tree has to be reconstructed everytime a little bit changes
> 
> In short: The tree-view interface does not come across cells-like at 
> all.   And I guess that is, because the way of looking at it is just not 
> a cells  way.

OK, I understand. You are right, we want the tree-view to update 
automatically as the model changes, by which I mean either the 
population of the model or the value of a displayed slot of a node of 
the model, and normally this is how my GUIs work. It should be easy to 
get this in cells-gtk.

I may have to build Cells-gtk. Possibly you have missed some existing 
mechanism in cells-gtk, so I have CCed that list. Or possibly the 
cells-gtk tree-view got implemented with a static model in mind and it 
is merely time to break that assumption. No big deal, assuming GTk's API 
has the chops (which I wager it does).

A question is how much work gets done on the Lisp side. Do we end up 
with one Lisp widget instance for each observed row? Better yet, for 
each field of each row? ie, the lisp tree-view runs down the model 
rooted in "roots" looking for kids of kids and then... hmmm, maybe not, 
and it looks like I better build Cells-gtk.

Normally what I do is mirror each thingy on the C side with a thingy on 
Lisp side, so there would be tree-view and then tree-view-row and maybe 
tree-view-row-field (or tree-view-cell in spreadsheet-ese). To make 
things super-flexible, I then make those types parameterizable, so I can 
make a custom tree-view-cell (by subclassing tree-view-cell) and then 
specify to tree-view that it should use that subclass as it is 
traversing my data model building up the Lisp tree-view.

At this point, of course, on the Lisp side anyway I can "see" cells-wise 
everything that is happening, including tree insertion/deletions and 
changes to slot values of model nodes. The next question is how to tell GTk.

Does Gtk give us the granularity to operate on the tree view, ie, does 
it allow us to say "remove this row" or change this field of this row to 
show this new value ("My child")? I think you said yes to this in your 
prior note. If so, the next question is if we have done the FFI for 
those bits of the API. :)

I'll stop here since I am just collecting info, but rest assured this is 
normal stuff and if the GTk API offers the hooks we should have no 
problem getting information to flow from the lisp side to Gtk, using the 
ideas hinted at above.

kt



> 
> So I came up with the idea to make the interface to the tree-view a 
> tree  of cells, so that in effect for every box that is displayed in 
> the  treeview there is a dependent cell in the treeview (Instead of one 
> cell  connecting to the kids of the root).
> 
> One way to do this would be to have an observer object
> 
> (defmodel tto () ((obs :accessor obs :initarg :obs)  
> (corresponding-point-in-gtk))
> (def-c-output obs ((self tto))
>    (call-gtk-and-set corresponding-point-in-gtk new-value))
> 
> Then we would make six of these, two per node (name and age) when roots 
> is  assigned:
> 
> (def-c-output roots ((self tree-view))
>    (mapcar (lambda (nd) (make-instance 'tto :obs (c? (md-name nd))  
> :corresponding... ...)) (roots self))
>    ;; of course we'll have to recurse into the kids and the names of 
> the  accessors
>    ;; are supplied somewhere to the treeview etc.
> 
> Additionally we'd have some sort of structure observer that listens on 
> the  kids of every node (here three for three nodes) and reacts to 
> changes by  adding/deleting rows to the tree-view. Additionally it'd 
> have to  create/remove tto's to listen to the slots of new nodes or stop 
> listening  to removed nodes.
> 
> 
> Another option would be what I dubbed the "family-observer":  Some 
> piece  of magic that gets notified when any slot in a model or any of 
> its  decendents gets modified.  The notification would idealy include 
> the  modified node, the modified slot, the new-value and a path from the 
> root  to the node (could be a closure, (lambda (n) (nth 2 (nth 3 (nth 0 
> (kids n]  => (lambda root) is the modified node, or just a list '(0 3 
> 2)).  That  family observer could then take this notification/message 
> and do the  appropriate action.
> 
> I do not understand cells enough to judge whether this would even be  
> possible.  So maybe there is something, some instance where all state  
> changes get passed through, that could filter out the ones going to 
> root  or its descendents, maybe not.
> 
> Well, I hope it's clearer now what I wish to do.
> 
> In reply to your comments:
> 
> On Tue, 11 Dec 2007 21:03:39 +0100, Ken Tilton 
> <kennytilton at optonline.net>  wrote:
> 
>> More below, just retro-inserting notes: normally in /my/ work the 
>> parent  slot is not even a Cell, but I /have/ done that a couple of 
>> times on  certain subclasses of Family and it did work.
> 
> 
> Yeah, thanks for pointing that out.  I got that confused from time to 
> time  and was wondering why a (c? parent) does not learn about (kids 
> parent).
> 
>> Confused: The kids slot is a Cell, so any rule anywhere (on slots not  
>> just the ascendants) that references (kids <whatever>) will get 
>> kicked  off whent that changes. More below on this.
> 
> 
> I was wondering whether there would be a way to kick of that rule when  
> something deeper down in the tree gets modified (see above, the family  
> observer)
> 
>>>  If you have seen my ton of mails on the cells-gtk-devel list, you 
>>> know  I'm  doing some GUI work with cells-gtk at the moment.  I ran 
>>> into the   following issue:  cells-gtk can use a family tree as a 
>>> natural   representation of the contents of a tree-view.  However, 
>>> when I change  the  items in that family tree, cells-gtk currently 
>>> has no way of  updating the  C data structure correspondingly.
>>
>>
>> This sentence seems crucial and puzzles me some. What precisely is 
>> meant  by "when I change the items in that family tree"? Do you mean 
>> reorgamize  the tree by moving kids between parents?
> 
> 
> Maybe this, maybe changing a cell slot on a node in the tree, maybe 
> adding  kids, maybe removing them. In effect I want to keep the 
> projection of the  tree onto the tree-view consistent with the tree.
> 
>> As for "update the C data structure correspondingly", well, /what/ C  
>> data structure, this is the first I have heard of it. Do you mean the 
>> C  struct implementing the tree view, or a C struct one might be  
>> "inspecting" by mapping its hierarchy to a tree of widgets?
> 
> 
> The gtk tree-view displays an internal data structure, consisting of 
> rows  of "cells".  rows can have children.  Whenever we want to put some 
> lisp  structure in the treeview, we have to traverse it and build the  
> corresponding gtk stuff.
> 
>> Let me clarify a couple of things first. btw, I do not even have  
>> Cells-Gtk installed anywhere so I may have to do that if things get 
>> too  complicated.
> 
> 
> I hope it won't come that far :) I really appreciate your help, esp. 
> given  you won't even use cells-gtk.
> 
>> First of all, my models normally do not have the parent slot as a 
>> Cell  at all, meaning I do not move things from one parent to another. 
>> But!  There have been times when I did that and it did seem to work, 
>> so let's  keep that in mind going forward.
> 
> 
> Probably we won't have to.  I believe this one comes out of a  
> misunderstanding -- or am I not getting it?
> 
>> Second, without looking I am almost certain the kids slot of the 
>> family  class already does let applications react to changes to kids, 
>> so I am  not clear on why TTO is necessary (unless we are talking 
>> about also  needing the parent to be a Cell, which as I said might 
>> Just Work if we  put our minds to it).
> 
> 
> This confuses me.  "the kids slot of the family class already does let  
> applications react to changes to kids" -- what exactly do you mean by  
> that?  Adding/removing kids?  Changes to the kids themselves (setf  
> (md-name (first (kids root))) "Joe")?
> 
> To me it looks like neither happens.
> 
> (defparameter root (make-instance 'node :md-name "Root" :kids (c-in nil)))
>   => ROOT
> (defparameter tview (make-instance 'tree-view :roots (c? (kids root))))
>   => TVIEW
> (push (make-instance 'node :md-name "child") (kids root))
>   => (child)
> 
> ====> Nothing
> 
> (roots tview)
>   => ("roots have changed" (child))
>   => (child)
> 
> (i.e. the def-c-observer only gets called once I query the slot)
> 
>> Third, I'd like to understand the functionality better. Is the goal 
>> to  manipulate a tree on the C side via a [Cells-]Gtk tree view? Or 
>> just  dynamically restucture a treeview? This is the same question as 
>> above  where I ask about what is meant by "update the C data structure"?
> 
> 
> The goal is
> 
> I modify lisp stuff
>   ==>  the GTK tree model stuff ("C data structure") gets updated
>           ==> The treeview reflects my changes to the lisp stuff
> 
> It looks like I got myself into quite a mess here ...
> 
> Cheers,
> Peter
> 




More information about the cells-devel mailing list