5.5.7 DTreeFrame.oz

Functor DTreeFrame (see also Section A.13) provides functionality to graphically display a dependency tree, even one that is only partially constructed. The ability to display a partially constructed dependency tree is particularly nice to visualize the state of the constraints at a distribution point (choice point) in the search tree, i.e. the nodes displayed as blue circles in the Explorer.

import Tk
export 'class' : DtreeFrame
   DB = o(
           bg   : ivory
           word : o(font : o(family:helvetica
                             size  :12)
                    color: black)
           label: o(font : o(family:fixed
                             size  :12)
                    color: black)
           vstep: 15
           hstep: 5
           margin : o(top:20 left:20)
           vline: o(color:orange width:2)
           sline: o(color:slateblue width:2)
   class Font from Tk.font 
      attr ascent descent
      meth tkInit(...) = M
         ascent <- {Tk.returnInt font(metrics self ascent:unit)}
         descent<- {Tk.returnInt font(metrics self descent:unit)}
      meth measure(Text $)
         {Tk.returnInt font(measure self Text)}
      meth getAscent($) @ascent end 
      meth getDescent($) @descent end 
   WordFont = {New Font tkInit(family:DB.word.font.family
                               size  :DB.word.font.size)}
   LabelFont= {New Font tkInit(family:DB.label.font.family
                               size  :DB.label.font.size)}
   class DtreeFrame from Tk.frame 
      attr canvas tag
      meth tkInit(...)=M
         canvas <- {New Tk.canvas tkInit(parent:self bg:DB.bg)}
         tag    <- {New Tk.canvasTag tkInit(parent:@canvas)}
         H = {New Tk.scrollbar tkInit(parent:self orient:horizontal)}
         V = {New Tk.scrollbar tkInit(parent:self orient:vertical)}
         {@canvas tk(configure scrollregion:q(0 0 0 0))}
         {Tk.addXScrollbar @canvas H}
         {Tk.addYScrollbar @canvas V}
         {Tk.batch [grid(rowconfigure self 0 weight:1)
                    grid(columnconfigure self 0 weight:1)
                    grid(@canvas row:0 column:0 sticky:nswe)
                    grid(H row:1 column:0 sticky:we)
                    grid(V row:0 column:1 sticky:ns)]}
      meth clear {@canvas tk(delete @tag)} end 
      meth show(L)
         {self clear}
         %% L is a list of elements of the form
         %% o(string:WORD index:INDEX parent:INDEX2 label:LABEL)
         Nodes = {Map L
                  fun {$ X}
                     o(string:X.string label:X.label
                       index:X.index parent:{CondSelect X parent unit}
                       height:_ left:_
                              {WordFont  measure(X.string $)}
                              {LabelFont measure(X.label  $)}})
         NodesR = {List.toRecord o {Map Nodes fun {$ N} N.index#end}}
         %% we compute how high to place each node
         fun {Height N}
            if {IsDet N.height} then N.height
            elseif N.parent==unit then N.height=1
            else N.height=(1+{Height NodesR.(N.parent)}) end 
         MaxHeight =
         {FoldL Nodes fun {$ Accu N} {Max Accu {Height N}} end 0}
         Top = MaxHeight*DB.vstep+DB.margin.top
         %% compute left coord of each node
         ScrollWidth =
         {FoldL Nodes
          fun {$ Left N} N.left=Left Left+N.width+DB.hstep end 
         ScrollHeight =
         Top+DB.margin.top+{WordFont getDescent($)}+{WordFont getAscent($)}
         {@canvas tk(configure scrollregion:q(0 0 ScrollWidth ScrollHeight))}
         %% create slanted lines
         {ForAll Nodes
          proc {$ N}
             if N.parent\=unit then 
                P  = NodesR.(N.parent)
                X1 = N.left+(N.width div 2)
                Y1 = N.height*DB.vstep
                X2 = P.left+(P.width div 2)
                Y2 = P.height*DB.vstep
                {@canvas tk(create line X1 Y1 X2 Y2 fill:DB.sline.color
                            width:DB.vline.width tags:@tag)}
         %% create text items, vertical lines, and labels
         {ForAll Nodes
          proc {$ N}
             X = N.left+(N.width div 2)
             Y = N.height*DB.vstep
             {@canvas tk(create line X Top X Y fill:DB.vline.color
                         width:DB.vline.width tags:@tag)}
             {@canvas tk(create text X Top fill:DB.word.color
                         font:WordFont anchor:n text:N.string tags:@tag)}
             {@canvas tk(create text X Y fill:DB.label.color
                         font:LabelFont anchor:s text:N.label tags:@tag)}
      in skip end 

Denys Duchier
Version 1.2.0 (20010221)